github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/server/peer_server_handlers.go (about) 1 package server 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "net/url" 7 "strconv" 8 "time" 9 10 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 11 "github.com/coreos/etcd/third_party/github.com/gorilla/mux" 12 13 etcdErr "github.com/coreos/etcd/error" 14 "github.com/coreos/etcd/log" 15 uhttp "github.com/coreos/etcd/pkg/http" 16 "github.com/coreos/etcd/store" 17 ) 18 19 // Get all the current logs 20 func (ps *PeerServer) GetLogHttpHandler(w http.ResponseWriter, req *http.Request) { 21 log.Debugf("[recv] GET %s/log", ps.Config.URL) 22 w.Header().Set("Content-Type", "application/json") 23 w.WriteHeader(http.StatusOK) 24 json.NewEncoder(w).Encode(ps.raftServer.LogEntries()) 25 } 26 27 // Response to vote request 28 func (ps *PeerServer) VoteHttpHandler(w http.ResponseWriter, req *http.Request) { 29 rvreq := &raft.RequestVoteRequest{} 30 31 if _, err := rvreq.Decode(req.Body); err != nil { 32 http.Error(w, "", http.StatusBadRequest) 33 log.Warnf("[recv] BADREQUEST %s/vote [%v]", ps.Config.URL, err) 34 return 35 } 36 37 log.Debugf("[recv] POST %s/vote [%s]", ps.Config.URL, rvreq.CandidateName) 38 39 resp := ps.raftServer.RequestVote(rvreq) 40 41 if resp == nil { 42 log.Warn("[vote] Error: nil response") 43 http.Error(w, "", http.StatusInternalServerError) 44 return 45 } 46 47 if _, err := resp.Encode(w); err != nil { 48 log.Warn("[vote] Error: %v", err) 49 http.Error(w, "", http.StatusInternalServerError) 50 return 51 } 52 } 53 54 // Response to append entries request 55 func (ps *PeerServer) AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) { 56 start := time.Now() 57 aereq := &raft.AppendEntriesRequest{} 58 59 if _, err := aereq.Decode(req.Body); err != nil { 60 http.Error(w, "", http.StatusBadRequest) 61 log.Warnf("[recv] BADREQUEST %s/log/append [%v]", ps.Config.URL, err) 62 return 63 } 64 65 log.Debugf("[recv] POST %s/log/append [%d]", ps.Config.URL, len(aereq.Entries)) 66 67 ps.serverStats.RecvAppendReq(aereq.LeaderName, int(req.ContentLength)) 68 69 resp := ps.raftServer.AppendEntries(aereq) 70 71 if resp == nil { 72 log.Warn("[ae] Error: nil response") 73 http.Error(w, "", http.StatusInternalServerError) 74 return 75 } 76 77 if !resp.Success() { 78 log.Debugf("[Append Entry] Step back") 79 } 80 81 if _, err := resp.Encode(w); err != nil { 82 log.Warn("[ae] Error: %v", err) 83 http.Error(w, "", http.StatusInternalServerError) 84 return 85 } 86 87 (*ps.metrics).Timer("timer.appendentries.handle").UpdateSince(start) 88 } 89 90 // Response to recover from snapshot request 91 func (ps *PeerServer) SnapshotHttpHandler(w http.ResponseWriter, req *http.Request) { 92 ssreq := &raft.SnapshotRequest{} 93 94 if _, err := ssreq.Decode(req.Body); err != nil { 95 http.Error(w, "", http.StatusBadRequest) 96 log.Warnf("[recv] BADREQUEST %s/snapshot [%v]", ps.Config.URL, err) 97 return 98 } 99 100 log.Debugf("[recv] POST %s/snapshot", ps.Config.URL) 101 102 resp := ps.raftServer.RequestSnapshot(ssreq) 103 104 if resp == nil { 105 log.Warn("[ss] Error: nil response") 106 http.Error(w, "", http.StatusInternalServerError) 107 return 108 } 109 110 if _, err := resp.Encode(w); err != nil { 111 log.Warn("[ss] Error: %v", err) 112 http.Error(w, "", http.StatusInternalServerError) 113 return 114 } 115 } 116 117 // Response to recover from snapshot request 118 func (ps *PeerServer) SnapshotRecoveryHttpHandler(w http.ResponseWriter, req *http.Request) { 119 ssrreq := &raft.SnapshotRecoveryRequest{} 120 121 if _, err := ssrreq.Decode(req.Body); err != nil { 122 http.Error(w, "", http.StatusBadRequest) 123 log.Warnf("[recv] BADREQUEST %s/snapshotRecovery [%v]", ps.Config.URL, err) 124 return 125 } 126 127 log.Debugf("[recv] POST %s/snapshotRecovery", ps.Config.URL) 128 129 resp := ps.raftServer.SnapshotRecoveryRequest(ssrreq) 130 131 if resp == nil { 132 log.Warn("[ssr] Error: nil response") 133 http.Error(w, "", http.StatusInternalServerError) 134 return 135 } 136 137 if _, err := resp.Encode(w); err != nil { 138 log.Warn("[ssr] Error: %v", err) 139 http.Error(w, "", http.StatusInternalServerError) 140 return 141 } 142 } 143 144 // Get the port that listening for etcd connecting of the server 145 func (ps *PeerServer) EtcdURLHttpHandler(w http.ResponseWriter, req *http.Request) { 146 log.Debugf("[recv] Get %s/etcdURL/ ", ps.Config.URL) 147 w.WriteHeader(http.StatusOK) 148 w.Write([]byte(ps.server.URL())) 149 } 150 151 // Response to the join request 152 func (ps *PeerServer) JoinHttpHandler(w http.ResponseWriter, req *http.Request) { 153 command := &JoinCommandV1{} 154 if err := uhttp.DecodeJsonRequest(req, command); err != nil { 155 w.WriteHeader(http.StatusInternalServerError) 156 return 157 } 158 159 log.Debugf("Receive Join Request from %s", command.Name) 160 err := ps.server.Dispatch(command, w, req) 161 162 // Return status. 163 if err != nil { 164 if etcdErr, ok := err.(*etcdErr.Error); ok { 165 log.Debug("Return error: ", (*etcdErr).Error()) 166 etcdErr.Write(w) 167 } else { 168 http.Error(w, err.Error(), http.StatusInternalServerError) 169 } 170 } 171 } 172 173 // Attempt to rejoin the cluster as a peer. 174 func (ps *PeerServer) PromoteHttpHandler(w http.ResponseWriter, req *http.Request) { 175 log.Infof("%s attempting to promote in cluster: %s", ps.Config.Name, ps.proxyPeerURL) 176 url, err := url.Parse(ps.proxyPeerURL) 177 if err != nil { 178 w.WriteHeader(http.StatusInternalServerError) 179 return 180 } 181 182 err = ps.joinByPeer(ps.raftServer, url.Host, ps.Config.Scheme) 183 if err != nil { 184 log.Infof("%s error while promoting: %v", ps.Config.Name, err) 185 w.WriteHeader(http.StatusInternalServerError) 186 return 187 } 188 log.Infof("%s promoted in the cluster", ps.Config.Name) 189 w.WriteHeader(http.StatusOK) 190 } 191 192 // Response to remove request 193 func (ps *PeerServer) RemoveHttpHandler(w http.ResponseWriter, req *http.Request) { 194 if req.Method != "DELETE" { 195 w.WriteHeader(http.StatusMethodNotAllowed) 196 return 197 } 198 199 vars := mux.Vars(req) 200 command := &RemoveCommandV1{ 201 Name: vars["name"], 202 } 203 204 log.Debugf("[recv] Remove Request [%s]", command.Name) 205 206 ps.server.Dispatch(command, w, req) 207 } 208 209 // Returns a JSON-encoded cluster configuration. 210 func (ps *PeerServer) getClusterConfigHttpHandler(w http.ResponseWriter, req *http.Request) { 211 json.NewEncoder(w).Encode(&ps.clusterConfig) 212 } 213 214 // Updates the cluster configuration. 215 func (ps *PeerServer) setClusterConfigHttpHandler(w http.ResponseWriter, req *http.Request) { 216 // Decode map. 217 m := make(map[string]interface{}) 218 if err := json.NewDecoder(req.Body).Decode(&m); err != nil { 219 http.Error(w, err.Error(), http.StatusInternalServerError) 220 return 221 } 222 223 // Copy config and update fields passed in. 224 config := &ClusterConfig{ 225 ActiveSize: ps.clusterConfig.ActiveSize, 226 PromoteDelay: ps.clusterConfig.PromoteDelay, 227 } 228 if activeSize, ok := m["activeSize"].(float64); ok { 229 config.ActiveSize = int(activeSize) 230 } 231 if promoteDelay, ok := m["promoteDelay"].(float64); ok { 232 config.PromoteDelay = int(promoteDelay) 233 } 234 235 // Issue command to update. 236 c := &SetClusterConfigCommand{Config: config} 237 log.Debugf("[recv] Update Cluster Config Request") 238 ps.server.Dispatch(c, w, req) 239 240 json.NewEncoder(w).Encode(&ps.clusterConfig) 241 } 242 243 // Retrieves a list of peers and proxies. 244 func (ps *PeerServer) getMachinesHttpHandler(w http.ResponseWriter, req *http.Request) { 245 machines := make([]*machineMessage, 0) 246 for _, name := range ps.registry.Peers() { 247 machines = append(machines, ps.getMachineMessage(name)) 248 } 249 for _, name := range ps.registry.Proxies() { 250 machines = append(machines, ps.getMachineMessage(name)) 251 } 252 json.NewEncoder(w).Encode(&machines) 253 } 254 255 // Retrieve single peer or proxy. 256 func (ps *PeerServer) getMachineHttpHandler(w http.ResponseWriter, req *http.Request) { 257 vars := mux.Vars(req) 258 json.NewEncoder(w).Encode(ps.getMachineMessage(vars["name"])) 259 } 260 261 func (ps *PeerServer) getMachineMessage(name string) *machineMessage { 262 if ps.registry.PeerExists(name) { 263 clientURL, _ := ps.registry.ClientURL(name) 264 peerURL, _ := ps.registry.PeerURL(name) 265 return &machineMessage{ 266 Name: name, 267 Mode: PeerMode, 268 ClientURL: clientURL, 269 PeerURL: peerURL, 270 } 271 } 272 273 if ps.registry.ProxyExists(name) { 274 clientURL, _ := ps.registry.ProxyClientURL(name) 275 peerURL, _ := ps.registry.ProxyPeerURL(name) 276 return &machineMessage{ 277 Name: name, 278 Mode: ProxyMode, 279 ClientURL: clientURL, 280 PeerURL: peerURL, 281 } 282 } 283 284 return nil 285 } 286 287 // Adds a machine to the cluster. 288 func (ps *PeerServer) addMachineHttpHandler(w http.ResponseWriter, req *http.Request) { 289 c := &JoinCommandV2{} 290 if err := uhttp.DecodeJsonRequest(req, c); err != nil { 291 w.WriteHeader(http.StatusInternalServerError) 292 return 293 } 294 295 log.Debugf("Receive Join Request (v2) from %s", c.Name) 296 if err := ps.server.Dispatch(c, w, req); err != nil { 297 if etcdErr, ok := err.(*etcdErr.Error); ok { 298 log.Debug("Return error: ", (*etcdErr).Error()) 299 etcdErr.Write(w) 300 } else { 301 http.Error(w, err.Error(), http.StatusInternalServerError) 302 } 303 } 304 } 305 306 // Removes a machine from the cluster. 307 func (ps *PeerServer) removeMachineHttpHandler(w http.ResponseWriter, req *http.Request) { 308 vars := mux.Vars(req) 309 c := &RemoveCommandV2{Name: vars["name"]} 310 log.Debugf("[recv] Remove Request [%s]", c.Name) 311 ps.server.Dispatch(c, w, req) 312 } 313 314 // Response to the name request 315 func (ps *PeerServer) NameHttpHandler(w http.ResponseWriter, req *http.Request) { 316 log.Debugf("[recv] Get %s/name/ ", ps.Config.URL) 317 w.WriteHeader(http.StatusOK) 318 w.Write([]byte(ps.Config.Name)) 319 } 320 321 // Response to the name request 322 func (ps *PeerServer) VersionHttpHandler(w http.ResponseWriter, req *http.Request) { 323 log.Debugf("[recv] Get %s/version/ ", ps.Config.URL) 324 w.WriteHeader(http.StatusOK) 325 w.Write([]byte(strconv.Itoa(ps.store.Version()))) 326 } 327 328 // Checks whether a given version is supported. 329 func (ps *PeerServer) VersionCheckHttpHandler(w http.ResponseWriter, req *http.Request) { 330 log.Debugf("[recv] Get %s%s ", ps.Config.URL, req.URL.Path) 331 vars := mux.Vars(req) 332 version, _ := strconv.Atoi(vars["version"]) 333 if version >= store.MinVersion() && version <= store.MaxVersion() { 334 w.WriteHeader(http.StatusOK) 335 } else { 336 w.WriteHeader(http.StatusForbidden) 337 } 338 } 339 340 // Upgrades the current store version to the next version. 341 func (ps *PeerServer) UpgradeHttpHandler(w http.ResponseWriter, req *http.Request) { 342 log.Debugf("[recv] Get %s/version", ps.Config.URL) 343 344 // Check if upgrade is possible for all nodes. 345 if err := ps.Upgradable(); err != nil { 346 http.Error(w, err.Error(), http.StatusInternalServerError) 347 return 348 } 349 350 // Create an upgrade command from the current version. 351 c := ps.store.CommandFactory().CreateUpgradeCommand() 352 if err := ps.server.Dispatch(c, w, req); err != nil { 353 http.Error(w, err.Error(), http.StatusInternalServerError) 354 return 355 } 356 357 w.WriteHeader(http.StatusOK) 358 } 359 360 // machineMessage represents information about a peer or proxy in the registry. 361 type machineMessage struct { 362 Name string `json:"name"` 363 Mode Mode `json:"mode"` 364 ClientURL string `json:"clientURL"` 365 PeerURL string `json:"peerURL"` 366 }