*:discovery hook up

release-2.0
Xiang Li 2014-10-04 20:20:45 +08:00 committed by Brandon Philips
parent 824b7231b8
commit 9e3d045b2b
5 changed files with 112 additions and 63 deletions

View File

@ -14,7 +14,7 @@ import (
"github.com/coreos/etcd/third_party/code.google.com/p/go.net/context" "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
) )
const ( var (
v2Prefix = "/v2/keys" v2Prefix = "/v2/keys"
) )
@ -47,12 +47,18 @@ func NewHTTPClient(tr *http.Transport, ep string, timeout time.Duration) (*httpC
return c, nil return c, nil
} }
func (c *httpClient) SetPrefix(p string) {
v2Prefix = p
}
func (c *httpClient) Create(key, val string, ttl time.Duration) (*Response, error) { func (c *httpClient) Create(key, val string, ttl time.Duration) (*Response, error) {
uintTTL := uint64(ttl.Seconds())
create := &createAction{ create := &createAction{
Key: key, Key: key,
Value: val, Value: val,
TTL: &uintTTL, }
if ttl >= 0 {
uttl := uint64(ttl.Seconds())
create.TTL = &uttl
} }
ctx, cancel := context.WithTimeout(context.Background(), c.timeout) ctx, cancel := context.WithTimeout(context.Background(), c.timeout)

View File

@ -3,13 +3,15 @@ package discovery
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/http"
"net/url"
"path" "path"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/coreos/etcd/client" "github.com/coreos/etcd/client"
"github.com/coreos/etcd/etcdserver/etcdhttp"
) )
var ( var (
@ -21,40 +23,66 @@ var (
ErrFullCluster = errors.New("discovery: cluster is full") ErrFullCluster = errors.New("discovery: cluster is full")
) )
type Discoverer interface {
Discover() (string, error)
}
type discovery struct { type discovery struct {
cluster string cluster string
id int64 id int64
ctx []byte config string
c client.Client c client.Client
} }
func (d *discovery) discover() (*etcdhttp.Peers, error) { func New(durl string, id int64, config string) (Discoverer, error) {
u, err := url.Parse(durl)
if err != nil {
return nil, err
}
token := u.Path
u.Path = ""
client, err := client.NewHTTPClient(&http.Transport{}, u.String(), time.Second*5)
if err != nil {
return nil, err
}
// discovery service redirects /[key] to /v2/keys/[key]
// set the prefix of client to "" to handle this
client.SetPrefix("")
return &discovery{
cluster: token,
id: id,
config: config,
c: client,
}, nil
}
func (d *discovery) Discover() (string, error) {
// fast path: if the cluster is full, returns the error // fast path: if the cluster is full, returns the error
// do not need to register itself to the cluster in this // do not need to register itself to the cluster in this
// case. // case.
if _, _, err := d.checkCluster(); err != nil { if _, _, err := d.checkCluster(); err != nil {
return nil, err return "", err
} }
if err := d.createSelf(); err != nil { if err := d.createSelf(); err != nil {
return nil, err return "", err
} }
nodes, size, err := d.checkCluster() nodes, size, err := d.checkCluster()
if err != nil { if err != nil {
return nil, err return "", err
} }
all, err := d.waitNodes(nodes, size) all, err := d.waitNodes(nodes, size)
if err != nil { if err != nil {
return nil, err return "", err
} }
return nodesToPeers(all) return nodesToCluster(all), nil
} }
func (d *discovery) createSelf() error { func (d *discovery) createSelf() error {
resp, err := d.c.Create(d.selfKey(), string(d.ctx), 0) resp, err := d.c.Create(d.selfKey(), d.config, -1)
if err != nil { if err != nil {
return err return err
} }
@ -87,7 +115,7 @@ func (d *discovery) checkCluster() (client.Nodes, int, error) {
nodes := make(client.Nodes, 0) nodes := make(client.Nodes, 0)
// append non-config keys to nodes // append non-config keys to nodes
for _, n := range resp.Node.Nodes { for _, n := range resp.Node.Nodes {
if !strings.HasPrefix(n.Key, configKey) { if !strings.Contains(n.Key, configKey) {
nodes = append(nodes, n) nodes = append(nodes, n)
} }
} }
@ -97,7 +125,7 @@ func (d *discovery) checkCluster() (client.Nodes, int, error) {
// find self position // find self position
for i := range nodes { for i := range nodes {
if nodes[i].Key == d.selfKey() { if strings.Contains(nodes[i].Key, d.selfKey()) {
break break
} }
if i >= size-1 { if i >= size-1 {
@ -111,7 +139,7 @@ func (d *discovery) waitNodes(nodes client.Nodes, size int) (client.Nodes, error
if len(nodes) > size { if len(nodes) > size {
nodes = nodes[:size] nodes = nodes[:size]
} }
w := d.c.RecursiveWatch(d.cluster, nodes[len(nodes)-1].ModifiedIndex) w := d.c.RecursiveWatch(d.cluster, nodes[len(nodes)-1].ModifiedIndex+1)
all := make(client.Nodes, len(nodes)) all := make(client.Nodes, len(nodes))
copy(all, nodes) copy(all, nodes)
// wait for others // wait for others
@ -129,17 +157,12 @@ func (d *discovery) selfKey() string {
return path.Join("/", d.cluster, fmt.Sprintf("%d", d.id)) return path.Join("/", d.cluster, fmt.Sprintf("%d", d.id))
} }
func nodesToPeers(ns client.Nodes) (*etcdhttp.Peers, error) { func nodesToCluster(ns client.Nodes) string {
s := make([]string, len(ns)) s := make([]string, len(ns))
for i, n := range ns { for i, n := range ns {
s[i] = n.Value s[i] = n.Value
} }
return strings.Join(s, ",")
var peers etcdhttp.Peers
if err := peers.Set(strings.Join(s, "&")); err != nil {
return nil, err
}
return &peers, nil
} }
type sortableNodes struct{ client.Nodes } type sortableNodes struct{ client.Nodes }

View File

@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/coreos/etcd/client" "github.com/coreos/etcd/client"
"github.com/coreos/etcd/etcdserver/etcdhttp"
) )
func TestCheckCluster(t *testing.T) { func TestCheckCluster(t *testing.T) {
@ -216,40 +215,17 @@ func TestCreateSelf(t *testing.T) {
} }
} }
func TestNodesToPeers(t *testing.T) { func TestNodesToCluster(t *testing.T) {
nodes := client.Nodes{ nodes := client.Nodes{
{Key: "/1000/1", Value: "1=1.1.1.1", CreatedIndex: 1}, {Key: "/1000/1", Value: "1=1.1.1.1", CreatedIndex: 1},
{Key: "/1000/2", Value: "2=2.2.2.2", CreatedIndex: 2}, {Key: "/1000/2", Value: "2=2.2.2.2", CreatedIndex: 2},
{Key: "/1000/3", Value: "3=3.3.3.3", CreatedIndex: 3}, {Key: "/1000/3", Value: "3=3.3.3.3", CreatedIndex: 3},
} }
w := &etcdhttp.Peers{} w := "1=1.1.1.1,2=2.2.2.2,3=3.3.3.3"
w.Set("1=1.1.1.1&2=2.2.2.2&3=3.3.3.3")
badnodes := client.Nodes{{Key: "1000/1", Value: "1=1.1.1.1&???", CreatedIndex: 1}} cluster := nodesToCluster(nodes)
if !reflect.DeepEqual(cluster, w) {
tests := []struct { t.Errorf("cluster = %v, want %v", cluster, w)
ns client.Nodes
wp *etcdhttp.Peers
we bool
}{
{nodes, w, false},
{badnodes, nil, true},
}
for i, tt := range tests {
peers, err := nodesToPeers(tt.ns)
if tt.we {
if err == nil {
t.Fatalf("#%d: err = %v, want not nil", i, err)
}
} else {
if err != nil {
t.Fatalf("#%d: err = %v, want nil", i, err)
}
}
if !reflect.DeepEqual(peers, tt.wp) {
t.Errorf("#%d: peers = %v, want %v", i, peers, tt.wp)
}
} }
} }

View File

@ -11,6 +11,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/coreos/etcd/discovery"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft" "github.com/coreos/etcd/raft"
@ -84,12 +85,13 @@ type RaftTimer interface {
} }
type ServerConfig struct { type ServerConfig struct {
Name string Name string
ClientURLs types.URLs DiscoveryURL string
DataDir string ClientURLs types.URLs
SnapCount int64 DataDir string
Cluster *Cluster SnapCount int64
Transport *http.Transport Cluster *Cluster
Transport *http.Transport
} }
// NewServer creates a new EtcdServer from the supplied configuration. The // NewServer creates a new EtcdServer from the supplied configuration. The
@ -111,6 +113,19 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
var err error var err error
waldir := path.Join(cfg.DataDir, "wal") waldir := path.Join(cfg.DataDir, "wal")
if !wal.Exist(waldir) { if !wal.Exist(waldir) {
if cfg.DiscoveryURL != "" {
d, err := discovery.New(cfg.DiscoveryURL, m.ID, cfg.Cluster.String())
if err != nil {
log.Fatalf("etcd: cannot init discovery %v", err)
}
s, err := d.Discover()
if err != nil {
log.Fatalf("etcd: %v", err)
}
if err = cfg.Cluster.Set(s); err != nil {
log.Fatalf("etcd: %v", err)
}
}
if w, err = wal.Create(waldir); err != nil { if w, err = wal.Create(waldir); err != nil {
log.Fatal(err) log.Fatal(err)
} }

41
main.go
View File

@ -27,6 +27,7 @@ const (
var ( var (
name = flag.String("name", "default", "Unique human-readable name for this node") name = flag.String("name", "default", "Unique human-readable name for this node")
dir = flag.String("data-dir", "", "Path to the data directory") dir = flag.String("data-dir", "", "Path to the data directory")
durl = flag.String("discovery", "", "Discovery service used to bootstrap the cluster")
snapCount = flag.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot") snapCount = flag.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
printVersion = flag.Bool("version", false, "Print the version and exit") printVersion = flag.Bool("version", false, "Print the version and exit")
@ -97,6 +98,9 @@ func main() {
} }
pkg.SetFlagsFromEnv(flag.CommandLine) pkg.SetFlagsFromEnv(flag.CommandLine)
if err := setClusterForDiscovery(); err != nil {
log.Fatalf("etcd: %v", err)
}
if string(*proxyFlag) == flagtypes.ProxyValueOff { if string(*proxyFlag) == flagtypes.ProxyValueOff {
startEtcd() startEtcd()
@ -137,12 +141,13 @@ func startEtcd() {
log.Fatal(err.Error()) log.Fatal(err.Error())
} }
cfg := &etcdserver.ServerConfig{ cfg := &etcdserver.ServerConfig{
Name: *name, Name: *name,
ClientURLs: acurls, ClientURLs: acurls,
DataDir: *dir, DataDir: *dir,
SnapCount: int64(*snapCount), SnapCount: int64(*snapCount),
Cluster: cluster, Cluster: cluster,
Transport: pt, Transport: pt,
DiscoveryURL: *durl,
} }
s := etcdserver.NewServer(cfg) s := etcdserver.NewServer(cfg)
s.Start() s.Start()
@ -231,3 +236,27 @@ func startProxy() {
}() }()
} }
} }
// setClusterForDiscovery sets cluster to a temporary value if you are using
// the discovery.
func setClusterForDiscovery() error {
set := make(map[string]bool)
flag.Visit(func(f *flag.Flag) {
set[f.Name] = true
})
if set["discovery"] && set["bootstrap-config"] {
return fmt.Errorf("both discovery and bootstrap-config are set")
}
if set["discovery"] {
apurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-peer-urls", "addr", peerTLSInfo)
if err != nil {
return err
}
addrs := make([]string, len(apurls))
for i := range apurls {
addrs[i] = apurls[i].String()
}
cluster.Set(fmt.Sprintf("%s=%s", *name, strings.Join(addrs, ",")))
}
return nil
}