// applyConfChange applies a ConfChange to the server. It is only // invoked with a ConfChange that has already passed through Raft func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.ConfState) (bool, error) { if err := s.Cluster.ValidateConfigurationChange(cc); err != nil { cc.NodeID = raft.None s.node.ApplyConfChange(cc) return false, err } *confState = *s.node.ApplyConfChange(cc) switch cc.Type { case raftpb.ConfChangeAddNode: m := new(Member) if err := json.Unmarshal(cc.Context, m); err != nil { log.Panicf("unmarshal member should never fail: %v", err) } if cc.NodeID != uint64(m.ID) { log.Panicf("nodeID should always be equal to member ID") } s.Cluster.AddMember(m) if m.ID == s.id { log.Printf("etcdserver: added local member %s %v to cluster %s", m.ID, m.PeerURLs, s.Cluster.ID()) } else { s.sendhub.Add(m) log.Printf("etcdserver: added member %s %v to cluster %s", m.ID, m.PeerURLs, s.Cluster.ID()) } case raftpb.ConfChangeRemoveNode: id := types.ID(cc.NodeID) s.Cluster.RemoveMember(id) if id == s.id { log.Printf("etcdserver: removed local member %s from cluster %s", id, s.Cluster.ID()) log.Println("etcdserver: the data-dir used by this member must be removed so that this host can be re-added with a new member ID") return true, nil } else { s.sendhub.Remove(id) log.Printf("etcdserver: removed member %s from cluster %s", id, s.Cluster.ID()) } case raftpb.ConfChangeUpdateNode: m := new(Member) if err := json.Unmarshal(cc.Context, m); err != nil { log.Panicf("unmarshal member should never fail: %v", err) } if cc.NodeID != uint64(m.ID) { log.Panicf("nodeID should always be equal to member ID") } s.Cluster.UpdateMember(m) if m.ID == s.id { log.Printf("etcdserver: update local member %s %v in cluster %s", m.ID, m.PeerURLs, s.Cluster.ID()) } else { s.sendhub.Update(m) log.Printf("etcdserver: update member %s %v in cluster %s", m.ID, m.PeerURLs, s.Cluster.ID()) } } return false, nil }
// applyConfChange applies a ConfChange to the server. It is only // invoked with a ConfChange that has already passed through Raft func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.ConfState) (bool, error) { if err := s.cluster.ValidateConfigurationChange(cc); err != nil { cc.NodeID = raft.None s.r.ApplyConfChange(cc) return false, err } *confState = *s.r.ApplyConfChange(cc) switch cc.Type { case raftpb.ConfChangeAddNode: m := new(membership.Member) if err := json.Unmarshal(cc.Context, m); err != nil { plog.Panicf("unmarshal member should never fail: %v", err) } if cc.NodeID != uint64(m.ID) { plog.Panicf("nodeID should always be equal to member ID") } s.cluster.AddMember(m) if m.ID == s.id { plog.Noticef("added local member %s %v to cluster %s", m.ID, m.PeerURLs, s.cluster.ID()) } else { s.r.transport.AddPeer(m.ID, m.PeerURLs) plog.Noticef("added member %s %v to cluster %s", m.ID, m.PeerURLs, s.cluster.ID()) } case raftpb.ConfChangeRemoveNode: id := types.ID(cc.NodeID) s.cluster.RemoveMember(id) if id == s.id { return true, nil } else { s.r.transport.RemovePeer(id) plog.Noticef("removed member %s from cluster %s", id, s.cluster.ID()) } case raftpb.ConfChangeUpdateNode: m := new(membership.Member) if err := json.Unmarshal(cc.Context, m); err != nil { plog.Panicf("unmarshal member should never fail: %v", err) } if cc.NodeID != uint64(m.ID) { plog.Panicf("nodeID should always be equal to member ID") } s.cluster.UpdateRaftAttributes(m.ID, m.RaftAttributes) if m.ID == s.id { plog.Noticef("update local member %s %v in cluster %s", m.ID, m.PeerURLs, s.cluster.ID()) } else { s.r.transport.UpdatePeer(m.ID, m.PeerURLs) plog.Noticef("update member %s %v in cluster %s", m.ID, m.PeerURLs, s.cluster.ID()) } } return false, nil }
func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, nodes []uint64) error { if err := s.checkConfChange(cc, nodes); err != nil { cc.NodeID = raft.None s.node.ApplyConfChange(cc) return err } s.node.ApplyConfChange(cc) switch cc.Type { case raftpb.ConfChangeAddNode: m := new(Member) if err := json.Unmarshal(cc.Context, m); err != nil { panic("unexpected unmarshal error") } if cc.NodeID != m.ID { panic("unexpected nodeID mismatch") } s.Cluster.AddMember(m) case raftpb.ConfChangeRemoveNode: s.Cluster.RemoveMember(cc.NodeID) } return nil }
func TestApplyConfChangeShouldStop(t *testing.T) { cl := membership.NewCluster("") cl.SetStore(store.New()) for i := 1; i <= 3; i++ { cl.AddMember(&membership.Member{ID: types.ID(i)}) } srv := &EtcdServer{ id: 1, r: raftNode{ Node: newNodeNop(), transport: rafthttp.NewNopTransporter(), }, cluster: cl, } cc := raftpb.ConfChange{ Type: raftpb.ConfChangeRemoveNode, NodeID: 2, } // remove non-local member shouldStop, err := srv.applyConfChange(cc, &raftpb.ConfState{}) if err != nil { t.Fatalf("unexpected error %v", err) } if shouldStop { t.Errorf("shouldStop = %t, want %t", shouldStop, false) } // remove local member cc.NodeID = 1 shouldStop, err = srv.applyConfChange(cc, &raftpb.ConfState{}) if err != nil { t.Fatalf("unexpected error %v", err) } if !shouldStop { t.Errorf("shouldStop = %t, want %t", shouldStop, true) } }