github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/server/join_command.go (about) 1 package server 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 7 etcdErr "github.com/coreos/etcd/error" 8 "github.com/coreos/etcd/log" 9 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 10 ) 11 12 func init() { 13 raft.RegisterCommand(&JoinCommandV1{}) 14 raft.RegisterCommand(&JoinCommandV2{}) 15 } 16 17 // JoinCommandV1 represents a request to join the cluster. 18 // The command returns the join_index (Uvarint). 19 type JoinCommandV1 struct { 20 MinVersion int `json:"minVersion"` 21 MaxVersion int `json:"maxVersion"` 22 Name string `json:"name"` 23 RaftURL string `json:"raftURL"` 24 EtcdURL string `json:"etcdURL"` 25 } 26 27 // The name of the join command in the log 28 func (c *JoinCommandV1) CommandName() string { 29 return "etcd:join" 30 } 31 32 func (c *JoinCommandV1) updatePeerURL(ps *PeerServer) error { 33 log.Debugf("Update peer URL of %v to %v", c.Name, c.RaftURL) 34 if err := ps.registry.UpdatePeerURL(c.Name, c.RaftURL); err != nil { 35 log.Debugf("Error while updating in registry: %s (%v)", c.Name, err) 36 return err 37 } 38 // Flush commit index, so raft will replay to here when restarted 39 ps.raftServer.FlushCommitIndex() 40 return nil 41 } 42 43 // Join a server to the cluster 44 func (c *JoinCommandV1) Apply(context raft.Context) (interface{}, error) { 45 ps, _ := context.Server().Context().(*PeerServer) 46 47 b := make([]byte, 8) 48 binary.PutUvarint(b, context.CommitIndex()) 49 50 // Make sure we're not getting a cached value from the registry. 51 ps.registry.Invalidate(c.Name) 52 53 // Check if the join command is from a previous peer, who lost all its previous log. 54 if peerURL, ok := ps.registry.PeerURL(c.Name); ok { 55 // If previous node restarts with different peer URL, 56 // update its information. 57 if peerURL != c.RaftURL { 58 log.Infof("Rejoin with %v instead of %v from %v", c.RaftURL, peerURL, c.Name) 59 if err := c.updatePeerURL(ps); err != nil { 60 return []byte{0}, err 61 } 62 } 63 return b, nil 64 } 65 66 // Check if the join command adds an instance that collides with existing one on peer URL. 67 peerURLs := ps.registry.PeerURLs(ps.raftServer.Leader(), c.Name) 68 for _, peerURL := range peerURLs { 69 if peerURL == c.RaftURL { 70 log.Warnf("%v tries to join the cluster with existing URL %v", c.Name, c.EtcdURL) 71 return []byte{0}, etcdErr.NewError(etcdErr.EcodeExistingPeerAddr, c.EtcdURL, context.CommitIndex()) 72 } 73 } 74 75 // Check peer number in the cluster 76 if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize { 77 log.Debug("Reject join request from ", c.Name) 78 return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMorePeer, "", context.CommitIndex()) 79 } 80 81 // Add to shared peer registry. 82 ps.registry.RegisterPeer(c.Name, c.RaftURL, c.EtcdURL) 83 84 // Add peer in raft 85 err := context.Server().AddPeer(c.Name, "") 86 87 // Add peer stats 88 if c.Name != ps.RaftServer().Name() { 89 ps.followersStats.Followers[c.Name] = &raftFollowerStats{} 90 ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63 91 } 92 93 return b, err 94 } 95 96 func (c *JoinCommandV1) NodeName() string { 97 return c.Name 98 } 99 100 // JoinCommandV2 represents a request to join the cluster. 101 type JoinCommandV2 struct { 102 MinVersion int `json:"minVersion"` 103 MaxVersion int `json:"maxVersion"` 104 Name string `json:"name"` 105 PeerURL string `json:"peerURL"` 106 ClientURL string `json:"clientURL"` 107 } 108 109 // CommandName returns the name of the command in the Raft log. 110 func (c *JoinCommandV2) CommandName() string { 111 return "etcd:v2:join" 112 } 113 114 func (c *JoinCommandV2) updatePeerURL(ps *PeerServer) error { 115 log.Debugf("Update peer URL of %v to %v", c.Name, c.PeerURL) 116 if err := ps.registry.UpdatePeerURL(c.Name, c.PeerURL); err != nil { 117 log.Debugf("Error while updating in registry: %s (%v)", c.Name, err) 118 return err 119 } 120 // Flush commit index, so raft will replay to here when restart 121 ps.raftServer.FlushCommitIndex() 122 return nil 123 } 124 125 // Apply attempts to join a machine to the cluster. 126 func (c *JoinCommandV2) Apply(context raft.Context) (interface{}, error) { 127 ps, _ := context.Server().Context().(*PeerServer) 128 var msg = joinMessageV2{ 129 Mode: PeerMode, 130 CommitIndex: context.CommitIndex(), 131 } 132 133 // Make sure we're not getting a cached value from the registry. 134 ps.registry.Invalidate(c.Name) 135 136 // Check if the join command is from a previous peer, who lost all its previous log. 137 if peerURL, ok := ps.registry.PeerURL(c.Name); ok { 138 // If previous node restarts with different peer URL, 139 // update its information. 140 if peerURL != c.PeerURL { 141 log.Infof("Rejoin with %v instead of %v from %v", c.PeerURL, peerURL, c.Name) 142 if err := c.updatePeerURL(ps); err != nil { 143 return []byte{0}, err 144 } 145 } 146 return json.Marshal(msg) 147 } 148 149 // Check if the join command adds an instance that collides with existing one on peer URL. 150 peerURLs := ps.registry.PeerURLs(ps.raftServer.Leader(), c.Name) 151 for _, peerURL := range peerURLs { 152 if peerURL == c.PeerURL { 153 log.Warnf("%v tries to join the cluster with existing URL %v", c.Name, c.PeerURL) 154 return []byte{0}, etcdErr.NewError(etcdErr.EcodeExistingPeerAddr, c.PeerURL, context.CommitIndex()) 155 } 156 } 157 158 // Check peer number in the cluster. 159 if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize { 160 log.Debug("Join as standby ", c.Name) 161 ps.registry.RegisterStandby(c.Name, c.PeerURL, c.ClientURL) 162 msg.Mode = StandbyMode 163 return json.Marshal(msg) 164 } 165 166 // Remove it as a standby if it is one. 167 if ps.registry.StandbyExists(c.Name) { 168 ps.registry.UnregisterStandby(c.Name) 169 } 170 171 // Add to shared peer registry. 172 ps.registry.RegisterPeer(c.Name, c.PeerURL, c.ClientURL) 173 174 // Add peer in raft 175 if err := context.Server().AddPeer(c.Name, ""); err != nil { 176 b, _ := json.Marshal(msg) 177 return b, err 178 } 179 180 // Add peer stats 181 if c.Name != ps.RaftServer().Name() { 182 ps.followersStats.Followers[c.Name] = &raftFollowerStats{} 183 ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63 184 } 185 186 return json.Marshal(msg) 187 } 188 189 func (c *JoinCommandV2) NodeName() string { 190 return c.Name 191 } 192 193 type joinMessageV2 struct { 194 CommitIndex uint64 `json:"commitIndex"` 195 Mode Mode `json:"mode"` 196 }