2015-01-25 06:19:16 +03:00
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-10-18 02:41:22 +04:00
2014-09-25 06:51:27 +04:00
package etcdserver
import (
2015-04-27 05:49:01 +03:00
"bytes"
2014-10-11 15:20:14 +04:00
"crypto/sha1"
"encoding/binary"
2014-10-23 03:15:37 +04:00
"encoding/json"
2014-09-25 06:51:27 +04:00
"fmt"
2014-10-23 22:57:42 +04:00
"path"
2014-09-25 06:51:27 +04:00
"sort"
"strings"
2014-11-05 23:40:51 +03:00
"sync"
2014-10-02 00:12:24 +04:00
2015-05-08 21:10:12 +03:00
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
2015-08-21 19:41:13 +03:00
"github.com/coreos/etcd/pkg/netutil"
2014-10-02 00:12:24 +04:00
"github.com/coreos/etcd/pkg/types"
2015-04-27 05:49:01 +03:00
"github.com/coreos/etcd/raft"
2014-11-06 04:16:42 +03:00
"github.com/coreos/etcd/raft/raftpb"
2014-10-23 03:15:37 +04:00
"github.com/coreos/etcd/store"
2015-08-15 18:30:06 +03:00
"github.com/coreos/etcd/version"
2014-09-25 06:51:27 +04:00
)
2014-10-23 03:15:37 +04:00
const (
2014-10-23 22:57:42 +04:00
raftAttributesSuffix = "raftAttributes"
attributesSuffix = "attributes"
2014-10-23 03:15:37 +04:00
)
2015-05-13 03:22:06 +03:00
type Cluster interface {
2014-11-10 23:55:35 +03:00
// ID returns the cluster ID
2014-10-31 03:31:14 +03:00
ID ( ) types . ID
2014-11-10 23:55:35 +03:00
// ClientURLs returns an aggregate set of all URLs on which this
// cluster is listening for client requests
2014-10-23 03:15:37 +04:00
ClientURLs ( ) [ ] string
2014-10-24 02:57:27 +04:00
// Members returns a slice of members sorted by their ID
Members ( ) [ ] * Member
2014-11-10 23:55:35 +03:00
// Member retrieves a particular member based on ID, or nil if the
// member does not exist in the cluster
2014-10-31 03:31:14 +03:00
Member ( id types . ID ) * Member
2014-11-10 23:55:35 +03:00
// IsIDRemoved checks whether the given ID has been removed from this
// cluster at some point in the past
2014-11-05 02:57:25 +03:00
IsIDRemoved ( id types . ID ) bool
2015-05-13 19:33:38 +03:00
// ClusterVersion is the cluster-wide minimum major.minor version.
Version ( ) * semver . Version
2014-10-23 03:15:37 +04:00
}
2014-09-25 06:51:27 +04:00
// Cluster is a list of Members that belong to the same raft cluster
2015-05-13 03:22:06 +03:00
type cluster struct {
2015-02-15 09:26:12 +03:00
id types . ID
token string
store store . Store
2015-05-08 21:10:12 +03:00
sync . Mutex // guards the fields below
version * semver . Version
2015-02-15 09:26:12 +03:00
members map [ types . ID ] * Member
2014-10-24 00:22:51 +04:00
// removed contains the ids of removed members in the cluster.
// removed id cannot be reused.
2014-10-31 03:31:14 +03:00
removed map [ types . ID ] bool
2014-10-11 15:20:14 +04:00
}
2015-05-13 03:22:06 +03:00
func newClusterFromURLsMap ( token string , urlsmap types . URLsMap ) ( * cluster , error ) {
2014-10-30 23:43:38 +03:00
c := newCluster ( token )
2015-05-13 03:22:06 +03:00
for name , urls := range urlsmap {
2015-04-27 05:49:01 +03:00
m := NewMember ( name , urls , token , nil )
2014-10-23 03:15:37 +04:00
if _ , ok := c . members [ m . ID ] ; ok {
2015-04-27 05:49:01 +03:00
return nil , fmt . Errorf ( "member exists with identical ID %v" , m )
}
if uint64 ( m . ID ) == raft . None {
return nil , fmt . Errorf ( "cannot use %x as member id" , raft . None )
2014-10-22 02:02:57 +04:00
}
2014-10-23 03:15:37 +04:00
c . members [ m . ID ] = m
2014-09-25 06:51:27 +04:00
}
2014-10-23 22:50:15 +04:00
c . genID ( )
2014-10-23 03:15:37 +04:00
return c , nil
2014-09-25 06:51:27 +04:00
}
2015-05-13 03:22:06 +03:00
func newClusterFromMembers ( token string , id types . ID , membs [ ] * Member ) * cluster {
2014-10-30 23:43:38 +03:00
c := newCluster ( token )
2014-10-24 03:41:58 +04:00
c . id = id
for _ , m := range membs {
c . members [ m . ID ] = m
}
return c
}
2015-05-13 03:22:06 +03:00
func newCluster ( token string ) * cluster {
return & cluster {
2014-10-30 23:43:38 +03:00
token : token ,
2014-10-31 03:31:14 +03:00
members : make ( map [ types . ID ] * Member ) ,
removed : make ( map [ types . ID ] bool ) ,
2014-09-25 06:51:27 +04:00
}
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) ID ( ) types . ID { return c . id }
2014-10-11 15:20:14 +04:00
2015-05-13 03:22:06 +03:00
func ( c * cluster ) Members ( ) [ ] * Member {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2015-05-21 19:02:33 +03:00
var ms MembersByID
2014-10-24 02:57:27 +04:00
for _ , m := range c . members {
2015-05-21 19:02:33 +03:00
ms = append ( ms , m . Clone ( ) )
2014-10-24 02:57:27 +04:00
}
2015-05-21 19:02:33 +03:00
sort . Sort ( ms )
return [ ] * Member ( ms )
2014-10-24 02:57:27 +04:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) Member ( id types . ID ) * Member {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-11-08 03:04:09 +03:00
return c . members [ id ] . Clone ( )
2014-10-23 03:15:37 +04:00
}
2014-10-24 00:22:51 +04:00
// MemberByName returns a Member with the given name if exists.
2014-10-24 01:24:07 +04:00
// If more than one member has the given name, it will panic.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) MemberByName ( name string ) * Member {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-10-24 01:24:07 +04:00
var memb * Member
2014-10-23 03:15:37 +04:00
for _ , m := range c . members {
if m . Name == name {
2014-10-24 01:24:07 +04:00
if memb != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "two members with the given name %q exist" , name )
2014-10-24 01:24:07 +04:00
}
memb = m
2014-10-23 03:15:37 +04:00
}
}
2014-11-08 03:04:09 +03:00
return memb . Clone ( )
2014-10-23 03:15:37 +04:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) MemberIDs ( ) [ ] types . ID {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-10-31 03:31:14 +03:00
var ids [ ] types . ID
2014-10-11 15:20:14 +04:00
for _ , m := range c . members {
2014-09-25 06:51:27 +04:00
ids = append ( ids , m . ID )
}
2014-10-31 03:31:14 +03:00
sort . Sort ( types . IDSlice ( ids ) )
2014-09-25 06:51:27 +04:00
return ids
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) IsIDRemoved ( id types . ID ) bool {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-10-23 03:15:37 +04:00
return c . removed [ id ]
}
2014-12-26 07:36:48 +03:00
// PeerURLs returns a list of all peer addresses.
// The returned list is sorted in ascending lexicographical order.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) PeerURLs ( ) [ ] string {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-12-26 07:36:48 +03:00
urls := make ( [ ] string , 0 )
2014-10-11 15:20:14 +04:00
for _ , p := range c . members {
2014-09-25 06:51:27 +04:00
for _ , addr := range p . PeerURLs {
2014-12-26 07:36:48 +03:00
urls = append ( urls , addr )
2014-09-25 06:51:27 +04:00
}
}
2014-12-26 07:36:48 +03:00
sort . Strings ( urls )
return urls
2014-09-25 06:51:27 +04:00
}
2014-09-27 03:50:15 +04:00
2014-12-26 07:36:48 +03:00
// ClientURLs returns a list of all client addresses.
// The returned list is sorted in ascending lexicographical order.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) ClientURLs ( ) [ ] string {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-09-27 03:50:15 +04:00
urls := make ( [ ] string , 0 )
2014-10-11 15:20:14 +04:00
for _ , p := range c . members {
2014-09-27 03:50:15 +04:00
for _ , url := range p . ClientURLs {
2014-10-01 02:49:13 +04:00
urls = append ( urls , url )
2014-09-27 03:50:15 +04:00
}
}
sort . Strings ( urls )
return urls
}
2014-10-23 03:15:37 +04:00
2015-05-13 03:22:06 +03:00
func ( c * cluster ) String ( ) string {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2015-04-27 05:49:01 +03:00
b := & bytes . Buffer { }
fmt . Fprintf ( b , "{ClusterID:%s " , c . id )
var ms [ ] string
2014-10-23 03:15:37 +04:00
for _ , m := range c . members {
2015-04-27 05:49:01 +03:00
ms = append ( ms , fmt . Sprintf ( "%+v" , m ) )
2014-10-23 03:15:37 +04:00
}
2015-04-27 05:49:01 +03:00
fmt . Fprintf ( b , "Members:[%s] " , strings . Join ( ms , " " ) )
var ids [ ] string
2015-08-22 04:52:16 +03:00
for id := range c . removed {
2015-04-27 05:49:01 +03:00
ids = append ( ids , fmt . Sprintf ( "%s" , id ) )
}
fmt . Fprintf ( b , "RemovedMemberIDs:[%s]}" , strings . Join ( ids , " " ) )
return b . String ( )
2014-10-23 03:15:37 +04:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) genID ( ) {
2014-10-23 03:15:37 +04:00
mIDs := c . MemberIDs ( )
b := make ( [ ] byte , 8 * len ( mIDs ) )
for i , id := range mIDs {
2014-10-31 03:31:14 +03:00
binary . BigEndian . PutUint64 ( b [ 8 * i : ] , uint64 ( id ) )
2014-10-23 03:15:37 +04:00
}
hash := sha1 . Sum ( b )
2014-10-31 03:31:14 +03:00
c . id = types . ID ( binary . BigEndian . Uint64 ( hash [ : 8 ] ) )
2014-10-23 03:15:37 +04:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) SetID ( id types . ID ) { c . id = id }
2014-10-23 03:15:37 +04:00
2015-05-13 03:22:06 +03:00
func ( c * cluster ) SetStore ( st store . Store ) { c . store = st }
2014-10-23 03:15:37 +04:00
2015-05-13 03:22:06 +03:00
func ( c * cluster ) Recover ( ) {
2015-10-27 06:26:43 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-11-14 22:45:03 +03:00
c . members , c . removed = membersFromStore ( c . store )
2015-05-08 21:10:12 +03:00
c . version = clusterVersionFromStore ( c . store )
2015-08-17 19:54:53 +03:00
MustDetectDowngrade ( c . version )
2015-10-03 00:02:29 +03:00
for _ , m := range c . members {
plog . Infof ( "added member %s %v to cluster %s from store" , m . ID , m . PeerURLs , c . id )
}
if c . version != nil {
plog . Infof ( "set the cluster version to %v from store" , version . Cluster ( c . version . String ( ) ) )
}
2014-11-14 22:45:03 +03:00
}
2014-11-06 23:09:21 +03:00
// ValidateConfigurationChange takes a proposed ConfChange and
// ensures that it is still valid.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) ValidateConfigurationChange ( cc raftpb . ConfChange ) error {
2014-11-06 23:09:21 +03:00
members , removed := membersFromStore ( c . store )
2014-11-11 22:46:49 +03:00
id := types . ID ( cc . NodeID )
if removed [ id ] {
2014-11-06 04:16:42 +03:00
return ErrIDRemoved
}
switch cc . Type {
case raftpb . ConfChangeAddNode :
2014-11-11 22:46:49 +03:00
if members [ id ] != nil {
2014-11-06 04:16:42 +03:00
return ErrIDExists
}
2014-11-06 10:12:48 +03:00
urls := make ( map [ string ] bool )
2014-11-06 23:09:21 +03:00
for _ , m := range members {
2014-11-06 10:12:48 +03:00
for _ , u := range m . PeerURLs {
urls [ u ] = true
}
}
m := new ( Member )
if err := json . Unmarshal ( cc . Context , m ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "unmarshal member should never fail: %v" , err )
2014-11-06 10:12:48 +03:00
}
for _ , u := range m . PeerURLs {
if urls [ u ] {
return ErrPeerURLexists
}
}
2014-11-06 04:16:42 +03:00
case raftpb . ConfChangeRemoveNode :
2014-11-11 22:46:49 +03:00
if members [ id ] == nil {
2014-11-06 04:16:42 +03:00
return ErrIDNotFound
}
2014-11-11 22:46:49 +03:00
case raftpb . ConfChangeUpdateNode :
if members [ id ] == nil {
return ErrIDNotFound
}
urls := make ( map [ string ] bool )
for _ , m := range members {
if m . ID == id {
continue
}
for _ , u := range m . PeerURLs {
urls [ u ] = true
}
}
m := new ( Member )
if err := json . Unmarshal ( cc . Context , m ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "unmarshal member should never fail: %v" , err )
2014-11-11 22:46:49 +03:00
}
for _ , u := range m . PeerURLs {
if urls [ u ] {
return ErrPeerURLexists
}
}
2014-11-06 04:16:42 +03:00
default :
2015-06-08 23:28:34 +03:00
plog . Panicf ( "ConfChange type should be either AddNode, RemoveNode or UpdateNode" )
2014-11-06 04:16:42 +03:00
}
return nil
}
2014-11-06 03:40:15 +03:00
// AddMember adds a new Member into the cluster, and saves the given member's
// raftAttributes into the store. The given member should have empty attributes.
2014-10-23 03:15:37 +04:00
// A Member with a matching id must not exist.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) AddMember ( m * Member ) {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-10-23 03:15:37 +04:00
b , err := json . Marshal ( m . RaftAttributes )
if err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "marshal raftAttributes should never fail: %v" , err )
2014-10-23 03:15:37 +04:00
}
2014-10-23 22:57:42 +04:00
p := path . Join ( memberStoreKey ( m . ID ) , raftAttributesSuffix )
if _ , err := c . store . Create ( p , false , string ( b ) , false , store . Permanent ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "create raftAttributes should never fail: %v" , err )
2014-10-23 03:15:37 +04:00
}
2015-04-19 21:08:03 +03:00
c . members [ m . ID ] = m
2014-10-23 03:15:37 +04:00
}
// RemoveMember removes a member from the store.
2014-10-24 00:22:51 +04:00
// The given id MUST exist, or the function panics.
2015-05-13 03:22:06 +03:00
func ( c * cluster ) RemoveMember ( id types . ID ) {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-10-23 03:15:37 +04:00
if _ , err := c . store . Delete ( memberStoreKey ( id ) , true , true ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "delete member should never fail: %v" , err )
2014-10-23 03:15:37 +04:00
}
2015-04-19 21:08:03 +03:00
delete ( c . members , id )
2014-10-23 03:15:37 +04:00
if _ , err := c . store . Create ( removedMemberStoreKey ( id ) , false , "" , false , store . Permanent ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "create removedMember should never fail: %v" , err )
2014-10-23 03:15:37 +04:00
}
2015-04-19 21:08:03 +03:00
c . removed [ id ] = true
2014-10-23 03:15:37 +04:00
}
2015-10-09 00:07:16 +03:00
func ( c * cluster ) UpdateAttributes ( id types . ID , attr Attributes ) bool {
2014-11-08 03:04:09 +03:00
c . Lock ( )
defer c . Unlock ( )
2015-06-09 08:23:41 +03:00
if m , ok := c . members [ id ] ; ok {
m . Attributes = attr
2015-10-09 00:07:16 +03:00
return true
2015-06-09 08:23:41 +03:00
}
_ , ok := c . removed [ id ]
if ok {
2015-10-09 00:07:16 +03:00
plog . Warningf ( "skipped updating attributes of removed member %s" , id )
2015-06-09 08:23:41 +03:00
} else {
plog . Panicf ( "error updating attributes of unknown member %s" , id )
}
2014-12-26 07:56:30 +03:00
// TODO: update store in this function
2015-10-09 00:07:16 +03:00
return false
2014-11-08 03:04:09 +03:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) UpdateRaftAttributes ( id types . ID , raftAttr RaftAttributes ) {
2014-11-11 22:46:49 +03:00
c . Lock ( )
defer c . Unlock ( )
2014-12-26 07:56:30 +03:00
b , err := json . Marshal ( raftAttr )
2014-11-11 22:46:49 +03:00
if err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "marshal raftAttributes should never fail: %v" , err )
2014-11-11 22:46:49 +03:00
}
2014-12-26 07:56:30 +03:00
p := path . Join ( memberStoreKey ( id ) , raftAttributesSuffix )
2014-11-11 22:46:49 +03:00
if _ , err := c . store . Update ( p , string ( b ) , store . Permanent ) ; err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "update raftAttributes should never fail: %v" , err )
2014-11-11 22:46:49 +03:00
}
2015-04-19 21:08:03 +03:00
c . members [ id ] . RaftAttributes = raftAttr
2014-11-11 22:46:49 +03:00
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) Version ( ) * semver . Version {
2015-05-08 21:10:12 +03:00
c . Lock ( )
defer c . Unlock ( )
if c . version == nil {
return nil
}
return semver . Must ( semver . NewVersion ( c . version . String ( ) ) )
}
2015-05-13 03:22:06 +03:00
func ( c * cluster ) SetVersion ( ver * semver . Version ) {
2015-05-08 21:10:12 +03:00
c . Lock ( )
defer c . Unlock ( )
if c . version != nil {
2015-08-15 18:30:06 +03:00
plog . Noticef ( "updated the cluster version from %v to %v" , version . Cluster ( c . version . String ( ) ) , version . Cluster ( ver . String ( ) ) )
2015-05-08 21:10:12 +03:00
} else {
2015-08-15 18:30:06 +03:00
plog . Noticef ( "set the initial cluster version to %v" , version . Cluster ( ver . String ( ) ) )
2015-05-08 21:10:12 +03:00
}
c . version = ver
2015-08-17 19:54:53 +03:00
MustDetectDowngrade ( c . version )
2015-05-08 21:10:12 +03:00
}
2015-09-10 09:45:34 +03:00
func ( c * cluster ) isReadyToAddNewMember ( ) bool {
nmembers := 1
nstarted := 0
for _ , member := range c . members {
if member . IsStarted ( ) {
nstarted ++
}
nmembers ++
}
2015-09-13 04:11:23 +03:00
if nstarted == 1 && nmembers == 2 {
2015-09-10 09:45:34 +03:00
// a case of adding a new node to 1-member cluster for restoring cluster data
// https://github.com/coreos/etcd/blob/master/Documentation/admin_guide.md#restoring-the-cluster
plog . Debugf ( "The number of started member is 1. This cluster can accept add member request." )
return true
}
nquorum := nmembers / 2 + 1
if nstarted < nquorum {
plog . Warningf ( "Reject add member request: the number of started member (%d) will be less than the quorum number of the cluster (%d)" , nstarted , nquorum )
return false
2015-09-16 07:18:05 +03:00
}
return true
}
func ( c * cluster ) isReadyToRemoveMember ( id uint64 ) bool {
nmembers := 0
nstarted := 0
for _ , member := range c . members {
if uint64 ( member . ID ) == id {
continue
}
if member . IsStarted ( ) {
nstarted ++
}
nmembers ++
}
nquorum := nmembers / 2 + 1
if nstarted < nquorum {
plog . Warningf ( "Reject remove member request: the number of started member (%d) will be less than the quorum number of the cluster (%d)" , nstarted , nquorum )
return false
2015-09-10 09:45:34 +03:00
}
return true
}
2014-11-06 02:56:43 +03:00
func membersFromStore ( st store . Store ) ( map [ types . ID ] * Member , map [ types . ID ] bool ) {
members := make ( map [ types . ID ] * Member )
removed := make ( map [ types . ID ] bool )
e , err := st . Get ( storeMembersPrefix , true , true )
if err != nil {
if isKeyNotFound ( err ) {
return members , removed
}
2015-06-08 23:28:34 +03:00
plog . Panicf ( "get storeMembers should never fail: %v" , err )
2014-11-06 02:56:43 +03:00
}
for _ , n := range e . Node . Nodes {
2015-08-27 23:24:47 +03:00
var m * Member
m , err = nodeToMember ( n )
2014-11-06 02:56:43 +03:00
if err != nil {
2015-06-08 23:28:34 +03:00
plog . Panicf ( "nodeToMember should never fail: %v" , err )
2014-11-06 02:56:43 +03:00
}
members [ m . ID ] = m
}
e , err = st . Get ( storeRemovedMembersPrefix , true , true )
if err != nil {
if isKeyNotFound ( err ) {
return members , removed
}
2015-06-08 23:28:34 +03:00
plog . Panicf ( "get storeRemovedMembers should never fail: %v" , err )
2014-11-06 02:56:43 +03:00
}
for _ , n := range e . Node . Nodes {
removed [ mustParseMemberIDFromKey ( n . Key ) ] = true
}
return members , removed
}
2015-05-08 21:10:12 +03:00
func clusterVersionFromStore ( st store . Store ) * semver . Version {
e , err := st . Get ( path . Join ( StoreClusterPrefix , "version" ) , false , false )
if err != nil {
if isKeyNotFound ( err ) {
return nil
}
2015-06-08 23:28:34 +03:00
plog . Panicf ( "unexpected error (%v) when getting cluster version from store" , err )
2015-05-08 21:10:12 +03:00
}
return semver . Must ( semver . NewVersion ( * e . Node . Value ) )
}
2014-11-18 01:08:45 +03:00
// ValidateClusterAndAssignIDs validates the local cluster by matching the PeerURLs
// with the existing cluster. If the validation succeeds, it assigns the IDs
// from the existing cluster to the local cluster.
// If the validation fails, an error will be returned.
2015-05-13 03:22:06 +03:00
func ValidateClusterAndAssignIDs ( local * cluster , existing * cluster ) error {
2014-11-18 01:08:45 +03:00
ems := existing . Members ( )
lms := local . Members ( )
if len ( ems ) != len ( lms ) {
return fmt . Errorf ( "member count is unequal" )
}
2015-05-21 19:02:33 +03:00
sort . Sort ( MembersByPeerURLs ( ems ) )
sort . Sort ( MembersByPeerURLs ( lms ) )
2014-11-18 01:08:45 +03:00
for i := range ems {
2015-08-21 19:41:13 +03:00
if ! netutil . URLStringsEqual ( ems [ i ] . PeerURLs , lms [ i ] . PeerURLs ) {
2014-11-18 01:08:45 +03:00
return fmt . Errorf ( "unmatched member while checking PeerURLs" )
}
lms [ i ] . ID = ems [ i ] . ID
}
local . members = make ( map [ types . ID ] * Member )
for _ , m := range lms {
local . members [ m . ID ] = m
}
return nil
}