mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
etcdserver: Calculate IDs for nodes solely on PeerURLs
Removes the notion of name being anything more than advisory or command-line grouping, and adds checks for bootstrapping the command line. IDs are consistent if the URLs are consistent.
This commit is contained in:
parent
e475388bc0
commit
456d1ebcae
@ -43,16 +43,6 @@ func (c Cluster) FindID(id uint64) *Member {
|
|||||||
return c.members[id]
|
return c.members[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Cluster) FindName(name string) *Member {
|
|
||||||
for _, m := range c.members {
|
|
||||||
if m.Name == name {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cluster) Add(m Member) error {
|
func (c Cluster) Add(m Member) error {
|
||||||
if c.FindID(m.ID) != nil {
|
if c.FindID(m.ID) != nil {
|
||||||
return fmt.Errorf("Member exists with identical ID %v", m)
|
return fmt.Errorf("Member exists with identical ID %v", m)
|
||||||
|
@ -137,7 +137,7 @@ func TestClusterFind(t *testing.T) {
|
|||||||
c := NewCluster()
|
c := NewCluster()
|
||||||
c.AddSlice(tt.mems)
|
c.AddSlice(tt.mems)
|
||||||
|
|
||||||
m := c.FindName(tt.name)
|
m := c.FindID(tt.id)
|
||||||
if m == nil && !tt.match {
|
if m == nil && !tt.match {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
|
// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Name string
|
LocalMember Member
|
||||||
DiscoveryURL string
|
DiscoveryURL string
|
||||||
ClientURLs types.URLs
|
ClientURLs types.URLs
|
||||||
DataDir string
|
DataDir string
|
||||||
@ -45,11 +45,16 @@ func (c *ServerConfig) VerifyBootstrapConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the cluster at least contains the local server.
|
// Make sure the cluster at least contains the local server.
|
||||||
m := c.Cluster.FindName(c.Name)
|
isOk := false
|
||||||
if m == nil {
|
for _, m := range c.Cluster.members {
|
||||||
return fmt.Errorf("could not find name %v in cluster", c.Name)
|
if m.ID == c.LocalMember.ID {
|
||||||
|
isOk = true
|
||||||
}
|
}
|
||||||
if m.ID == raft.None {
|
}
|
||||||
|
if !isOk {
|
||||||
|
return fmt.Errorf("couldn't find local ID in cluster config")
|
||||||
|
}
|
||||||
|
if c.LocalMember.ID == raft.None {
|
||||||
return fmt.Errorf("could not use %x as member id", raft.None)
|
return fmt.Errorf("could not use %x as member id", raft.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +63,7 @@ func (c *ServerConfig) VerifyBootstrapConfig() error {
|
|||||||
for _, m := range c.Cluster.Members() {
|
for _, m := range c.Cluster.Members() {
|
||||||
for _, url := range m.PeerURLs {
|
for _, url := range m.PeerURLs {
|
||||||
if urlMap[url] {
|
if urlMap[url] {
|
||||||
return fmt.Errorf("duplicate url %v in server config", url)
|
return fmt.Errorf("duplicate url %v in cluster config", url)
|
||||||
}
|
}
|
||||||
urlMap[url] = true
|
urlMap[url] = true
|
||||||
}
|
}
|
||||||
@ -70,7 +75,7 @@ func (c *ServerConfig) WALDir() string { return path.Join(c.DataDir, "wal") }
|
|||||||
|
|
||||||
func (c *ServerConfig) SnapDir() string { return path.Join(c.DataDir, "snap") }
|
func (c *ServerConfig) SnapDir() string { return path.Join(c.DataDir, "snap") }
|
||||||
|
|
||||||
func (c *ServerConfig) ID() uint64 { return c.Cluster.FindName(c.Name).ID }
|
func (c *ServerConfig) ID() uint64 { return c.LocalMember.ID }
|
||||||
|
|
||||||
func (c *ServerConfig) ShouldDiscover() bool {
|
func (c *ServerConfig) ShouldDiscover() bool {
|
||||||
return c.DiscoveryURL != ""
|
return c.DiscoveryURL != ""
|
||||||
|
@ -45,15 +45,22 @@ func TestBootstrapConfigVerify(t *testing.T) {
|
|||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
cluster := &Cluster{}
|
cluster := &Cluster{}
|
||||||
cluster.Set(tt.clusterSetting)
|
err := cluster.Set(tt.clusterSetting)
|
||||||
|
if err != nil && tt.shouldError {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
cfg := ServerConfig{
|
cfg := ServerConfig{
|
||||||
Name: "node1",
|
LocalMember: Member{
|
||||||
|
ID: 0x7350a9cd4dc16f76,
|
||||||
|
},
|
||||||
DiscoveryURL: tt.disc,
|
DiscoveryURL: tt.disc,
|
||||||
Cluster: cluster,
|
Cluster: cluster,
|
||||||
ClusterState: tt.clst,
|
ClusterState: tt.clst,
|
||||||
}
|
}
|
||||||
err := cfg.VerifyBootstrapConfig()
|
err = cfg.VerifyBootstrapConfig()
|
||||||
if (err == nil) && tt.shouldError {
|
if (err == nil) && tt.shouldError {
|
||||||
|
t.Errorf("%#v", *cluster)
|
||||||
t.Errorf("#%d: Got no error where one was expected", i)
|
t.Errorf("#%d: Got no error where one was expected", i)
|
||||||
}
|
}
|
||||||
if (err != nil) && !tt.shouldError {
|
if (err != nil) && !tt.shouldError {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -54,7 +55,8 @@ func newMember(name string, peerURLs types.URLs, now *time.Time) *Member {
|
|||||||
Attributes: Attributes{Name: name},
|
Attributes: Attributes{Name: name},
|
||||||
}
|
}
|
||||||
|
|
||||||
b := []byte(m.Name)
|
var b []byte
|
||||||
|
sort.Strings(m.PeerURLs)
|
||||||
for _, p := range m.PeerURLs {
|
for _, p := range m.PeerURLs {
|
||||||
b = append(b, []byte(p)...)
|
b = append(b, []byte(p)...)
|
||||||
}
|
}
|
||||||
@ -68,6 +70,10 @@ func newMember(name string, peerURLs types.URLs, now *time.Time) *Member {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMemberFromURLs(name string, urls types.URLs) *Member {
|
||||||
|
return newMember(name, urls, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (m Member) storeKey() string {
|
func (m Member) storeKey() string {
|
||||||
return path.Join(storeMembersPrefix, idAsHex(m.ID))
|
return path.Join(storeMembersPrefix, idAsHex(m.ID))
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,15 @@ func TestMemberTime(t *testing.T) {
|
|||||||
mem *Member
|
mem *Member
|
||||||
id uint64
|
id uint64
|
||||||
}{
|
}{
|
||||||
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, nil), 11240395089494390470},
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, nil), 14544069596553697298},
|
||||||
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}}, timeParse("1984-12-23T15:04:05Z")), 5483967913615174889},
|
// Same ID, different name (names shouldn't matter)
|
||||||
|
{newMember("memfoo", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, nil), 14544069596553697298},
|
||||||
|
// Same ID, different Time
|
||||||
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, timeParse("1984-12-23T15:04:05Z")), 2448790162483548276},
|
||||||
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}}, timeParse("1984-12-23T15:04:05Z")), 1466075294948436910},
|
||||||
|
// Order shouldn't matter
|
||||||
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "10.0.0.2:2379"}}, nil), 16552244735972308939},
|
||||||
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.2:2379"}, {Scheme: "http", Host: "10.0.0.1:2379"}}, nil), 16552244735972308939},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
if tt.mem.ID != tt.id {
|
if tt.mem.ID != tt.id {
|
||||||
|
@ -213,7 +213,7 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
|
|||||||
cls := &clusterStore{Store: st, id: cid}
|
cls := &clusterStore{Store: st, id: cid}
|
||||||
|
|
||||||
sstats := &stats.ServerStats{
|
sstats := &stats.ServerStats{
|
||||||
Name: cfg.Name,
|
Name: cfg.LocalMember.Attributes.Name,
|
||||||
ID: idAsHex(cfg.ID()),
|
ID: idAsHex(cfg.ID()),
|
||||||
}
|
}
|
||||||
lstats := stats.NewLeaderStats(idAsHex(cfg.ID()))
|
lstats := stats.NewLeaderStats(idAsHex(cfg.ID()))
|
||||||
@ -223,7 +223,7 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
|
|||||||
node: n,
|
node: n,
|
||||||
id: id,
|
id: id,
|
||||||
clusterID: cid,
|
clusterID: cid,
|
||||||
attributes: Attributes{Name: cfg.Name, ClientURLs: cfg.ClientURLs.StringSlice()},
|
attributes: Attributes{Name: cfg.LocalMember.Attributes.Name, ClientURLs: cfg.ClientURLs.StringSlice()},
|
||||||
storage: struct {
|
storage: struct {
|
||||||
*wal.WAL
|
*wal.WAL
|
||||||
*snap.Snapshotter
|
*snap.Snapshotter
|
||||||
|
@ -89,18 +89,21 @@ func (c *cluster) Launch(t *testing.T) {
|
|||||||
lns[i] = l
|
lns[i] = l
|
||||||
bootstrapCfgs[i] = fmt.Sprintf("%s=%s", c.name(i), "http://"+l.Addr().String())
|
bootstrapCfgs[i] = fmt.Sprintf("%s=%s", c.name(i), "http://"+l.Addr().String())
|
||||||
}
|
}
|
||||||
clusterCfg := &etcdserver.Cluster{}
|
clusterCfg := etcdserver.NewCluster()
|
||||||
if err := clusterCfg.Set(strings.Join(bootstrapCfgs, ",")); err != nil {
|
if err := clusterCfg.Set(strings.Join(bootstrapCfgs, ",")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
for i := 0; i < c.Size; i++ {
|
for i := 0; i < c.Size; i++ {
|
||||||
m := member{}
|
m := member{}
|
||||||
m.PeerListeners = []net.Listener{lns[i]}
|
m.PeerListeners = []net.Listener{lns[i]}
|
||||||
cln := newLocalListener(t)
|
cln := newLocalListener(t)
|
||||||
m.ClientListeners = []net.Listener{cln}
|
m.ClientListeners = []net.Listener{cln}
|
||||||
m.Name = c.name(i)
|
listenUrls, err := types.NewURLs([]string{"http://" + lns[i].Addr().String()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m.LocalMember = *etcdserver.NewMemberFromURLs(c.name(i), listenUrls)
|
||||||
m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()})
|
m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
61
main.go
61
main.go
@ -131,12 +131,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkg.SetFlagsFromEnv(fs)
|
pkg.SetFlagsFromEnv(fs)
|
||||||
if err := setClusterForDiscovery(); err != nil {
|
|
||||||
log.Fatalf("etcd: %v", err)
|
localMember, err := setupCluster()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("etcd: setupCluster returned error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(*proxyFlag) == flagtypes.ProxyValueOff {
|
if string(*proxyFlag) == flagtypes.ProxyValueOff {
|
||||||
startEtcd()
|
startEtcd(localMember)
|
||||||
} else {
|
} else {
|
||||||
startProxy()
|
startProxy()
|
||||||
}
|
}
|
||||||
@ -146,7 +148,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startEtcd launches the etcd server and HTTP handlers for client/server communication.
|
// startEtcd launches the etcd server and HTTP handlers for client/server communication.
|
||||||
func startEtcd() {
|
func startEtcd(self *etcdserver.Member) {
|
||||||
if *dir == "" {
|
if *dir == "" {
|
||||||
*dir = fmt.Sprintf("%v_etcd_data", *name)
|
*dir = fmt.Sprintf("%v_etcd_data", *name)
|
||||||
log.Printf("etcd: no data-dir provided, using default data-dir ./%s", *dir)
|
log.Printf("etcd: no data-dir provided, using default data-dir ./%s", *dir)
|
||||||
@ -165,7 +167,7 @@ func startEtcd() {
|
|||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
cfg := &etcdserver.ServerConfig{
|
cfg := &etcdserver.ServerConfig{
|
||||||
Name: *name,
|
LocalMember: *self,
|
||||||
ClientURLs: acurls,
|
ClientURLs: acurls,
|
||||||
DataDir: *dir,
|
DataDir: *dir,
|
||||||
SnapCount: *snapCount,
|
SnapCount: *snapCount,
|
||||||
@ -262,28 +264,53 @@ func startProxy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setClusterForDiscovery sets cluster to a temporary value if you are using
|
// setupCluster sets cluster to a temporary value if you are using
|
||||||
// the discovery.
|
// discovery, or to the static configuration for bootstrapped clusters.
|
||||||
func setClusterForDiscovery() error {
|
// Returns the local member on success.
|
||||||
|
func setupCluster() (*etcdserver.Member, error) {
|
||||||
set := make(map[string]bool)
|
set := make(map[string]bool)
|
||||||
|
var m *etcdserver.Member
|
||||||
flag.Visit(func(f *flag.Flag) {
|
flag.Visit(func(f *flag.Flag) {
|
||||||
set[f.Name] = true
|
set[f.Name] = true
|
||||||
})
|
})
|
||||||
if set["discovery"] && set["initial-cluster"] {
|
if set["discovery"] && set["initial-cluster"] {
|
||||||
return fmt.Errorf("both discovery and initial-cluster are set")
|
return nil, fmt.Errorf("both discovery and bootstrap-config are set")
|
||||||
}
|
}
|
||||||
if set["discovery"] {
|
if set["discovery"] {
|
||||||
apurls, err := pkg.URLsFromFlags(fs, "advertise-peer-urls", "peer-addr", peerTLSInfo)
|
apurls, err := pkg.URLsFromFlags(fs, "advertise-peer-urls", "peer-addr", peerTLSInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
addrs := make([]string, len(apurls))
|
m = etcdserver.NewMemberFromURLs(*name, apurls)
|
||||||
for i := range apurls {
|
cluster = etcdserver.NewCluster()
|
||||||
addrs[i] = fmt.Sprintf("%s=%s", *name, apurls[i].String())
|
cluster.Add(*m)
|
||||||
}
|
return m, nil
|
||||||
if err := cluster.Set(strings.Join(addrs, ",")); err != nil {
|
} else if set["initial-cluster"] {
|
||||||
return err
|
// We're statically configured, and cluster has appropriately been set.
|
||||||
|
// Try to configure by indexing the static cluster by name.
|
||||||
|
if set["name"] {
|
||||||
|
for _, c := range cluster.Members() {
|
||||||
|
if c.Name == *name {
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
log.Println("etcd: cannot find the passed name", *name, "in initial-cluster. Trying advertise-peer-urls")
|
||||||
|
}
|
||||||
|
// Try to configure by looking for a matching machine based on the peer urls.
|
||||||
|
if set["advertise-peer-urls"] {
|
||||||
|
apurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-peer-urls", "addr", peerTLSInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m = etcdserver.NewMemberFromURLs(*name, apurls)
|
||||||
|
for _, c := range cluster.Members() {
|
||||||
|
if c.ID == m.ID {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("etcd: Could not find a matching peer for the local instance in initial-cluster.")
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("etcd: Bootstrapping a static cluster, but local name or local peer urls are not defined")
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("etcd: Flag configuration not set for discovery or initial-cluster")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user