From dc3f7f5d903cedb20ebdaf9cf9fb4cea8bf298c0 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Thu, 2 Jul 2015 18:16:49 -0700 Subject: [PATCH] *: detect duplicate name for discovery bootstrap --- discovery/discovery.go | 19 ++++++++++---- discovery/discovery_test.go | 52 +++++++++++++++++++++++++++++++------ etcdmain/etcd.go | 6 ++++- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/discovery/discovery.go b/discovery/discovery.go index 72c4f0463..f089f60a5 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -42,6 +42,7 @@ var ( ErrSizeNotFound = errors.New("discovery: size key not found") ErrTokenNotFound = errors.New("discovery: token not found") ErrDuplicateID = errors.New("discovery: found duplicate id") + ErrDuplicateName = errors.New("discovery: found duplicate name") ErrFullCluster = errors.New("discovery: cluster is full") ErrTooManyRetries = errors.New("discovery: too many retries") ) @@ -170,14 +171,14 @@ func (d *discovery) joinCluster(config string) (string, error) { return "", err } - return nodesToCluster(all), nil + return nodesToCluster(all, size) } func (d *discovery) getCluster() (string, error) { nodes, size, index, err := d.checkCluster() if err != nil { if err == ErrFullCluster { - return nodesToCluster(nodes), nil + return nodesToCluster(nodes, size) } return "", err } @@ -186,7 +187,7 @@ func (d *discovery) getCluster() (string, error) { if err != nil { return "", err } - return nodesToCluster(all), nil + return nodesToCluster(all, size) } func (d *discovery) createSelf(contents string) error { @@ -322,12 +323,20 @@ func (d *discovery) selfKey() string { return path.Join("/", d.cluster, d.id.String()) } -func nodesToCluster(ns []*client.Node) string { +func nodesToCluster(ns []*client.Node, size int) (string, error) { s := make([]string, len(ns)) for i, n := range ns { s[i] = n.Value } - return strings.Join(s, ",") + us := strings.Join(s, ",") + m, err := types.NewURLsMap(us) + if err != nil { + return us, ErrInvalidURL + } + if m.Len() != size { + return us, ErrDuplicateName + } + return us, nil } type sortableNodes struct{ Nodes []*client.Node } diff --git a/discovery/discovery_test.go b/discovery/discovery_test.go index 16147a82b..30c88e3c7 100644 --- a/discovery/discovery_test.go +++ b/discovery/discovery_test.go @@ -344,16 +344,52 @@ func TestCreateSelf(t *testing.T) { } func TestNodesToCluster(t *testing.T) { - nodes := []*client.Node{ - 0: {Key: "/1000/1", Value: "1=1.1.1.1", CreatedIndex: 1}, - 1: {Key: "/1000/2", Value: "2=2.2.2.2", CreatedIndex: 2}, - 2: {Key: "/1000/3", Value: "3=3.3.3.3", CreatedIndex: 3}, + tests := []struct { + nodes []*client.Node + size int + wcluster string + werr error + }{ + { + []*client.Node{ + 0: {Key: "/1000/1", Value: "1=http://1.1.1.1:2380", CreatedIndex: 1}, + 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2}, + 2: {Key: "/1000/3", Value: "3=http://3.3.3.3:2380", CreatedIndex: 3}, + }, + 3, + "1=http://1.1.1.1:2380,2=http://2.2.2.2:2380,3=http://3.3.3.3:2380", + nil, + }, + { + []*client.Node{ + 0: {Key: "/1000/1", Value: "1=http://1.1.1.1:2380", CreatedIndex: 1}, + 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2}, + 2: {Key: "/1000/3", Value: "2=http://3.3.3.3:2380", CreatedIndex: 3}, + }, + 3, + "1=http://1.1.1.1:2380,2=http://2.2.2.2:2380,2=http://3.3.3.3:2380", + ErrDuplicateName, + }, + { + []*client.Node{ + 0: {Key: "/1000/1", Value: "1=1.1.1.1:2380", CreatedIndex: 1}, + 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2}, + 2: {Key: "/1000/3", Value: "2=http://3.3.3.3:2380", CreatedIndex: 3}, + }, + 3, + "1=1.1.1.1:2380,2=http://2.2.2.2:2380,2=http://3.3.3.3:2380", + ErrInvalidURL, + }, } - w := "1=1.1.1.1,2=2.2.2.2,3=3.3.3.3" - cluster := nodesToCluster(nodes) - if !reflect.DeepEqual(cluster, w) { - t.Errorf("cluster = %v, want %v", cluster, w) + for i, tt := range tests { + cluster, err := nodesToCluster(tt.nodes, tt.size) + if err != tt.werr { + t.Errorf("#%d: err = %v, want %v", i, err, tt.werr) + } + if !reflect.DeepEqual(cluster, tt.wcluster) { + t.Errorf("#%d: cluster = %v, want %v", i, cluster, tt.wcluster) + } } } diff --git a/etcdmain/etcd.go b/etcdmain/etcd.go index 5bdc28920..6b64e9d17 100644 --- a/etcdmain/etcd.go +++ b/etcdmain/etcd.go @@ -110,10 +110,14 @@ func Main() { switch err { case discovery.ErrDuplicateID: plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl) - plog.Errorf("But etcd could not find vaild cluster configuration in the given data dir (%s).", cfg.dir) + plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.dir) plog.Infof("Please check the given data dir path if the previous bootstrap succeeded") plog.Infof("or use a new discovery token if the previous bootstrap failed.") os.Exit(1) + case discovery.ErrDuplicateName: + plog.Errorf("member with duplicated name has registered with discovery service token(%s).", cfg.durl) + plog.Errorf("please check (cURL) the discovery token for more information.") + plog.Errorf("please do not reuse the discovery token and generate a new one to bootstrap the cluster.") default: plog.Fatalf("%v", err) }