2016-05-13 06:49:40 +03:00
// Copyright 2015 The etcd Authors
2015-01-25 06:19:16 +03:00
//
// 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
2016-04-07 22:02:37 +03:00
package membership
2014-09-25 06:51:27 +04:00
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
2016-04-25 22:32:58 +03:00
"github.com/coreos/etcd/mvcc/backend"
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"
2016-03-23 03:10:28 +03:00
"github.com/coreos/go-semver/semver"
2014-09-25 06:51:27 +04:00
)
2016-04-07 22:02:37 +03:00
// RaftCluster is a list of Members that belong to the same raft cluster
type RaftCluster struct {
2015-02-15 09:26:12 +03:00
id types . ID
token string
2016-04-08 02:30:34 +03:00
2015-02-15 09:26:12 +03:00
store store . Store
2016-04-08 02:30:34 +03:00
be backend . Backend
2015-02-15 09:26:12 +03:00
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
}
2016-04-07 22:02:37 +03:00
func NewClusterFromURLsMap ( token string , urlsmap types . URLsMap ) ( * RaftCluster , error ) {
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
}
2016-04-07 22:02:37 +03:00
func NewClusterFromMembers ( token string , id types . ID , membs [ ] * Member ) * RaftCluster {
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
}
2016-04-07 22:02:37 +03:00
func NewCluster ( token string ) * RaftCluster {
return & RaftCluster {
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
}
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) ID ( ) types . ID { return c . id }
2014-10-11 15:20:14 +04:00
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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 {
2016-04-08 08:09:25 +03:00
urls = append ( urls , p . PeerURLs ... )
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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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 {
2016-04-08 08:09:25 +03:00
urls = append ( urls , p . ClientURLs ... )
2014-09-27 03:50:15 +04:00
}
sort . Strings ( urls )
return urls
}
2014-10-23 03:15:37 +04:00
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) SetID ( id types . ID ) { c . id = id }
2014-10-23 03:15:37 +04:00
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) SetStore ( st store . Store ) { c . store = st }
2014-10-23 03:15:37 +04:00
2016-04-08 23:52:19 +03:00
func ( c * RaftCluster ) SetBackend ( be backend . Backend ) {
c . be = be
2016-04-12 21:37:22 +03:00
mustCreateBackendBuckets ( c . be )
2016-04-08 23:52:19 +03:00
}
2016-07-26 21:33:11 +03:00
func ( c * RaftCluster ) Recover ( onSet func ( * semver . Version ) ) {
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 )
2016-04-07 22:02:37 +03:00
mustDetectDowngrade ( c . version )
2016-07-26 21:33:11 +03:00
onSet ( 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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) AddMember ( m * Member ) {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2016-04-07 22:02:37 +03:00
if c . store != nil {
2016-04-08 02:30:34 +03:00
mustSaveMemberToStore ( c . store , m )
}
if c . be != nil {
mustSaveMemberToBackend ( c . be , m )
2014-10-23 03:15:37 +04:00
}
2016-04-08 02:30:34 +03:00
2015-04-19 21:08:03 +03:00
c . members [ m . ID ] = m
2016-06-01 07:12:41 +03:00
plog . Infof ( "added member %s %v to cluster %s" , m . ID , m . PeerURLs , c . id )
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.
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) RemoveMember ( id types . ID ) {
2014-11-05 23:40:51 +03:00
c . Lock ( )
defer c . Unlock ( )
2016-04-07 22:02:37 +03:00
if c . store != nil {
2016-04-08 02:30:34 +03:00
mustDeleteMemberFromStore ( c . store , id )
2014-10-23 03:15:37 +04:00
}
2016-04-08 02:30:34 +03:00
if c . be != nil {
mustDeleteMemberFromBackend ( c . be , id )
2014-10-23 03:15:37 +04:00
}
2016-04-08 02:30:34 +03:00
delete ( c . members , id )
2015-04-19 21:08:03 +03:00
c . removed [ id ] = true
2016-06-01 07:12:41 +03:00
plog . Infof ( "removed member %s from cluster %s" , id , c . id )
2014-10-23 03:15:37 +04:00
}
2016-04-08 05:12:00 +03:00
func ( c * RaftCluster ) UpdateAttributes ( id types . ID , attr Attributes ) {
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
2016-04-08 05:12:00 +03:00
if c . store != nil {
mustUpdateMemberAttrInStore ( c . store , m )
}
2016-04-08 23:14:37 +03:00
if c . be != nil {
mustSaveMemberToBackend ( c . be , m )
}
2016-04-08 05:12:00 +03:00
return
2015-06-09 08:23:41 +03:00
}
_ , ok := c . removed [ id ]
2016-04-08 05:12:00 +03:00
if ! ok {
2015-06-09 08:23:41 +03:00
plog . Panicf ( "error updating attributes of unknown member %s" , id )
}
2016-04-08 05:12:00 +03:00
plog . Warningf ( "skipped updating attributes of removed member %s" , id )
2014-11-08 03:04:09 +03:00
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) UpdateRaftAttributes ( id types . ID , raftAttr RaftAttributes ) {
2014-11-11 22:46:49 +03:00
c . Lock ( )
defer c . Unlock ( )
2016-04-07 22:02:37 +03:00
2016-04-08 02:30:34 +03:00
c . members [ id ] . RaftAttributes = raftAttr
2016-04-07 22:02:37 +03:00
if c . store != nil {
2016-04-08 02:30:34 +03:00
mustUpdateMemberInStore ( c . store , c . members [ id ] )
}
if c . be != nil {
mustSaveMemberToBackend ( c . be , c . members [ id ] )
2014-11-11 22:46:49 +03:00
}
2016-06-01 07:12:41 +03:00
plog . Noticef ( "updated member %s %v in cluster %s" , id , raftAttr . PeerURLs , c . id )
2014-11-11 22:46:49 +03:00
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) 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 ( ) ) )
}
2016-07-26 21:33:11 +03:00
func ( c * RaftCluster ) SetVersion ( ver * semver . Version , onSet func ( * 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
2016-04-07 22:02:37 +03:00
mustDetectDowngrade ( c . version )
2016-04-12 21:37:22 +03:00
if c . store != nil {
mustSaveClusterVersionToStore ( c . store , ver )
}
if c . be != nil {
mustSaveClusterVersionToBackend ( c . be , ver )
}
2016-07-26 21:33:11 +03:00
onSet ( ver )
2015-05-08 21:10:12 +03:00
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) IsReadyToAddNewMember ( ) bool {
2015-09-10 09:45:34 +03:00
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
2016-04-27 23:40:39 +03:00
// https://github.com/coreos/etcd/blob/master/Documentation/v2/admin_guide.md#restoring-the-cluster
2015-09-10 09:45:34 +03:00
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
}
2016-04-07 22:02:37 +03:00
func ( c * RaftCluster ) IsReadyToRemoveMember ( id uint64 ) bool {
2015-09-16 07:18:05 +03:00
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 )
2016-04-07 22:02:37 +03:00
e , err := st . Get ( StoreMembersPrefix , true , true )
2014-11-06 02:56:43 +03:00
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 {
2016-04-07 22:02:37 +03:00
removed [ MustParseMemberIDFromKey ( n . Key ) ] = true
2014-11-06 02:56:43 +03:00
}
return members , removed
}
2015-05-08 21:10:12 +03:00
func clusterVersionFromStore ( st store . Store ) * semver . Version {
2016-04-07 22:02:37 +03:00
e , err := st . Get ( path . Join ( storePrefix , "version" ) , false , false )
2015-05-08 21:10:12 +03:00
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.
2016-04-07 22:02:37 +03:00
func ValidateClusterAndAssignIDs ( local * RaftCluster , existing * RaftCluster ) 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
}
2016-04-07 22:02:37 +03:00
func mustDetectDowngrade ( cv * semver . Version ) {
lv := semver . Must ( semver . NewVersion ( version . Version ) )
// only keep major.minor version for comparison against cluster version
lv = & semver . Version { Major : lv . Major , Minor : lv . Minor }
if cv != nil && lv . LessThan ( * cv ) {
plog . Fatalf ( "cluster cannot be downgraded (current version: %s is lower than determined cluster version: %s)." , version . Version , version . Cluster ( cv . String ( ) ) )
}
}