// Copyright 2015 The etcd Authors // // 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. package membership import ( "crypto/sha1" "encoding/binary" "fmt" "math/rand" "sort" "time" "github.com/coreos/etcd/pkg/types" "github.com/coreos/pkg/capnslog" ) var ( plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "membership") ) // RaftAttributes represents the raft related attributes of an etcd member. type RaftAttributes struct { // PeerURLs is the list of peers in the raft cluster. // TODO(philips): ensure these are URLs PeerURLs []string `json:"peerURLs"` } // Attributes represents all the non-raft related attributes of an etcd member. type Attributes struct { Name string `json:"name,omitempty"` ClientURLs []string `json:"clientURLs,omitempty"` } type Member struct { ID types.ID `json:"id"` RaftAttributes Attributes } // NewMember creates a Member without an ID and generates one based on the // cluster name, peer URLs, and time. This is used for bootstrapping/adding new member. func NewMember(name string, peerURLs types.URLs, clusterName string, now *time.Time) *Member { m := &Member{ RaftAttributes: RaftAttributes{PeerURLs: peerURLs.StringSlice()}, Attributes: Attributes{Name: name}, } var b []byte sort.Strings(m.PeerURLs) for _, p := range m.PeerURLs { b = append(b, []byte(p)...) } b = append(b, []byte(clusterName)...) if now != nil { b = append(b, []byte(fmt.Sprintf("%d", now.Unix()))...) } hash := sha1.Sum(b) m.ID = types.ID(binary.BigEndian.Uint64(hash[:8])) return m } // PickPeerURL chooses a random address from a given Member's PeerURLs. // It will panic if there is no PeerURLs available in Member. func (m *Member) PickPeerURL() string { if len(m.PeerURLs) == 0 { plog.Panicf("member should always have some peer url") } return m.PeerURLs[rand.Intn(len(m.PeerURLs))] } func (m *Member) Clone() *Member { if m == nil { return nil } mm := &Member{ ID: m.ID, Attributes: Attributes{ Name: m.Name, }, } if m.PeerURLs != nil { mm.PeerURLs = make([]string, len(m.PeerURLs)) copy(mm.PeerURLs, m.PeerURLs) } if m.ClientURLs != nil { mm.ClientURLs = make([]string, len(m.ClientURLs)) copy(mm.ClientURLs, m.ClientURLs) } return mm } func (m *Member) IsStarted() bool { return len(m.Name) != 0 } // MembersByID implements sort by ID interface type MembersByID []*Member func (ms MembersByID) Len() int { return len(ms) } func (ms MembersByID) Less(i, j int) bool { return ms[i].ID < ms[j].ID } func (ms MembersByID) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } // MembersByPeerURLs implements sort by peer urls interface type MembersByPeerURLs []*Member func (ms MembersByPeerURLs) Len() int { return len(ms) } func (ms MembersByPeerURLs) Less(i, j int) bool { return ms[i].PeerURLs[0] < ms[j].PeerURLs[0] } func (ms MembersByPeerURLs) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }