Merge pull request #1537 from xiangli-cmu/cluster-token

Cluster token
release-2.0
Xiang Li 2014-10-30 17:09:25 -07:00
commit 9a56001d63
8 changed files with 35 additions and 32 deletions

View File

@ -5,7 +5,7 @@ When first started, etcd stores its configuration into the data directory. This
If a members data directory is ever lost or corrupted, the user should remove the etcd member from the cluster via the [members API][0]. (The member can then be re-added to the cluster with an empty data directory, again using the [members API][0], and it will recover state).
An etcd member restarted with previously used command line arguments but a new data directory is considered a different member. And it can potentially corrupt the cluster. If you are spinning up multiple clusters for testing it is recommended that you specify a unique cluster-name for the different clusters. This can protect you from cluster corruption in case of the misconfiguration metioned above.
An etcd member restarted with previously used command line arguments but a new data directory is considered a different member. And it can potentially corrupt the cluster. If you are spinning up multiple clusters for testing it is recommended that you specify a unique initial-cluster-token for the different clusters. This can protect you from cluster corruption in case of the misconfiguration metioned above.
The data directory has two sub-directories in it:

View File

@ -25,16 +25,19 @@ ETCD_INITIAL_CLUSTER_STATE=new
-initial-cluster-state new
```
If you are spinning up multiple clusters (or creating and destroying a single cluster) with same configuration for testing purpose, it is highly recommended that you specify a unique `initial-cluster-token` for the different clusters.
By doing this, etcd can generate unique cluster IDs and member IDs for the clusters even if they otherwise have the exact same configuration. This can protect you from cross-cluster-interaction, which might corrupt your clusters.
On each machine you would start etcd with these flags:
```
$ etcd -name infra0 -initial-advertise-peer-urls https://10.0.1.10:2379 \
$ etcd -name infra0 -initial-advertise-peer-urls https://10.0.1.10:2379 initial-cluster-token etcd-cluster-1\
-initial-cluster infra0=http://10.0.1.10:2379,infra1=http://10.0.1.11:2379,infra2=http://10.0.1.12:2379 \
-initial-cluster-state new
$ etcd -name infra1 -initial-advertise-peer-urls https://10.0.1.11:2379 \
$ etcd -name infra1 -initial-advertise-peer-urls https://10.0.1.11:2379 initial-cluster-token etcd-cluster-1\
-initial-cluster infra0=http://10.0.1.10:2379,infra1=http://10.0.1.11:2379,infra2=http://10.0.1.12:2379 \
-initial-cluster-state new
$ etcd -name infra2 -initial-advertise-peer-urls https://10.0.1.12:2379 \
$ etcd -name infra2 -initial-advertise-peer-urls https://10.0.1.12:2379 initial-cluster-token etcd-cluster-1\
-initial-cluster infra0=http://10.0.1.10:2379,infra1=http://10.0.1.11:2379,infra2=http://10.0.1.12:2379 \
-initial-cluster-state new
```

View File

@ -1,5 +1,5 @@
# Use goreman to run `go get github.com/mattn/goreman`
etcd1: bin/etcd -name node1 -listen-client-urls http://127.0.0.1:4001 -advertise-client-urls http://127.0.0.1:4001 -listen-peer-urls http://127.0.0.1:7001 -initial-advertise-peer-urls http://127.0.0.1:7001 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
etcd2: bin/etcd -name node2 -listen-client-urls http://127.0.0.1:4002 -advertise-client-urls http://127.0.0.1:4002 -listen-peer-urls http://127.0.0.1:7002 -initial-advertise-peer-urls http://127.0.0.1:7002 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
etcd3: bin/etcd -name node3 -listen-client-urls http://127.0.0.1:4003 -advertise-client-urls http://127.0.0.1:4003 -listen-peer-urls http://127.0.0.1:7003 -initial-advertise-peer-urls http://127.0.0.1:7003 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
etcd1: bin/etcd -name node1 -listen-client-urls http://127.0.0.1:4001 -advertise-client-urls http://127.0.0.1:4001 -listen-peer-urls http://127.0.0.1:7001 -initial-advertise-peer-urls http://127.0.0.1:7001 -initial-cluster-token etcd-cluster-1 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
etcd2: bin/etcd -name node2 -listen-client-urls http://127.0.0.1:4002 -advertise-client-urls http://127.0.0.1:4002 -listen-peer-urls http://127.0.0.1:7002 -initial-advertise-peer-urls http://127.0.0.1:7002 -initial-cluster-token etcd-cluster-1 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
etcd3: bin/etcd -name node3 -listen-client-urls http://127.0.0.1:4003 -advertise-client-urls http://127.0.0.1:4003 -listen-peer-urls http://127.0.0.1:7003 -initial-advertise-peer-urls http://127.0.0.1:7003 -initial-cluster-token etcd-cluster-1 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003' -initial-cluster-state new
proxy: bin/etcd -proxy=on -bind-addr 127.0.0.1:8080 -initial-cluster 'node1=http://localhost:7001,node2=http://localhost:7002,node3=http://localhost:7003'

View File

@ -48,9 +48,9 @@ var (
snapCount = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
printVersion = fs.Bool("version", false, "Print the version and exit")
initialCluster = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
initialClusterName = fs.String("initial-cluster-name", "etcd", "Initial name for the etcd cluster during bootstrap")
clusterState = new(etcdserver.ClusterState)
initialCluster = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
initialClusterToken = fs.String("initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap")
clusterState = new(etcdserver.ClusterState)
corsInfo = &cors.CORSInfo{}
proxyFlag = new(flags.Proxy)
@ -306,7 +306,7 @@ func setupCluster() (*etcdserver.Cluster, error) {
default:
// We're statically configured, and cluster has appropriately been set.
// Try to configure by indexing the static cluster by name.
cls, err = etcdserver.NewClusterFromString(*initialClusterName, *initialCluster)
cls, err = etcdserver.NewClusterFromString(*initialClusterToken, *initialCluster)
}
return cls, err
}

View File

@ -24,9 +24,9 @@ import (
func TestGenClusterString(t *testing.T) {
tests := []struct {
name string
urls []string
wstr string
token string
urls []string
wstr string
}{
{
"default", []string{"http://127.0.0.1:4001"},
@ -42,7 +42,7 @@ func TestGenClusterString(t *testing.T) {
if err != nil {
t.Fatalf("unexpected new urls error: %v", err)
}
str := genClusterString(tt.name, urls)
str := genClusterString(tt.token, urls)
if str != tt.wstr {
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.wstr)
}

View File

@ -50,7 +50,7 @@ type ClusterInfo interface {
// Cluster is a list of Members that belong to the same raft cluster
type Cluster struct {
id uint64
name string
token string
members map[uint64]*Member
// removed contains the ids of removed members in the cluster.
// removed id cannot be reused.
@ -58,11 +58,11 @@ type Cluster struct {
store store.Store
}
// NewClusterFromString returns Cluster through given clusterName and parsing
// NewClusterFromString returns Cluster through given cluster token and parsing
// members from a sets of names to IPs discovery formatted like:
// mach0=http://1.1.1.1,mach0=http://2.2.2.2,mach1=http://3.3.3.3,mach2=http://4.4.4.4
func NewClusterFromString(name string, cluster string) (*Cluster, error) {
c := newCluster(name)
func NewClusterFromString(token string, cluster string) (*Cluster, error) {
c := newCluster(token)
v, err := url.ParseQuery(strings.Replace(cluster, ",", "&", -1))
if err != nil {
@ -76,7 +76,7 @@ func NewClusterFromString(name string, cluster string) (*Cluster, error) {
if err := purls.Set(strings.Join(urls, ",")); err != nil {
return nil, err
}
m := NewMember(name, types.URLs(*purls), c.name, nil)
m := NewMember(name, types.URLs(*purls), c.token, nil)
if _, ok := c.members[m.ID]; ok {
return nil, fmt.Errorf("Member exists with identical ID %v", m)
}
@ -86,8 +86,8 @@ func NewClusterFromString(name string, cluster string) (*Cluster, error) {
return c, nil
}
func NewClusterFromStore(name string, st store.Store) *Cluster {
c := newCluster(name)
func NewClusterFromStore(token string, st store.Store) *Cluster {
c := newCluster(token)
c.store = st
e, err := c.store.Get(storeMembersPrefix, true, true)
@ -119,8 +119,8 @@ func NewClusterFromStore(name string, st store.Store) *Cluster {
return c
}
func NewClusterFromMembers(name string, id uint64, membs []*Member) *Cluster {
c := newCluster(name)
func NewClusterFromMembers(token string, id uint64, membs []*Member) *Cluster {
c := newCluster(token)
c.id = id
for _, m := range membs {
c.members[m.ID] = m
@ -128,9 +128,9 @@ func NewClusterFromMembers(name string, id uint64, membs []*Member) *Cluster {
return c
}
func newCluster(name string) *Cluster {
func newCluster(token string) *Cluster {
return &Cluster{
name: name,
token: token,
members: make(map[uint64]*Member),
removed: make(map[uint64]bool),
}

View File

@ -43,8 +43,8 @@ func TestClusterFromString(t *testing.T) {
if err != nil {
t.Fatalf("#%d: unexpected new error: %v", i, err)
}
if c.name != "abc" {
t.Errorf("#%d: name = %v, want abc", i, c.name)
if c.token != "abc" {
t.Errorf("#%d: token = %v, want abc", i, c.token)
}
wc := newTestCluster(tt.mems)
if !reflect.DeepEqual(c.members, wc.members) {
@ -99,8 +99,8 @@ func TestClusterFromStore(t *testing.T) {
hc.AddMember(&m)
}
c := NewClusterFromStore("abc", st)
if c.name != "abc" {
t.Errorf("#%d: name = %v, want %v", i, c.name, "abc")
if c.token != "abc" {
t.Errorf("#%d: token = %v, want %v", i, c.token, "abc")
}
wc := newTestCluster(tt.mems)
if !reflect.DeepEqual(c.members, wc.members) {

View File

@ -212,7 +212,7 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
if err != nil {
log.Fatalf("etcdserver: %v", err)
}
if cfg.Cluster, err = NewClusterFromString(cfg.Cluster.name, s); err != nil {
if cfg.Cluster, err = NewClusterFromString(cfg.Cluster.token, s); err != nil {
log.Fatalf("etcdserver: %v", err)
}
}
@ -232,7 +232,7 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
st.Recovery(snapshot.Data)
index = snapshot.Index
}
cfg.Cluster = NewClusterFromStore(cfg.Cluster.name, st)
cfg.Cluster = NewClusterFromStore(cfg.Cluster.token, st)
id, n, w = restartNode(cfg, index, snapshot)
default:
log.Fatalf("etcdserver: unsupported bootstrap config")