github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/server/server.go (about) 1 package server 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/http/pprof" 8 "strings" 9 "time" 10 11 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 12 "github.com/coreos/etcd/third_party/github.com/gorilla/mux" 13 14 etcdErr "github.com/coreos/etcd/error" 15 ehttp "github.com/coreos/etcd/http" 16 "github.com/coreos/etcd/log" 17 "github.com/coreos/etcd/metrics" 18 "github.com/coreos/etcd/mod" 19 uhttp "github.com/coreos/etcd/pkg/http" 20 "github.com/coreos/etcd/server/v1" 21 "github.com/coreos/etcd/server/v2" 22 "github.com/coreos/etcd/store" 23 _ "github.com/coreos/etcd/store/v2" 24 ) 25 26 // This is the default implementation of the Server interface. 27 type Server struct { 28 Name string 29 url string 30 handler http.Handler 31 peerServer *PeerServer 32 registry *Registry 33 store store.Store 34 metrics *metrics.Bucket 35 36 trace bool 37 } 38 39 // Creates a new Server. 40 func New(name, url string, peerServer *PeerServer, registry *Registry, store store.Store, mb *metrics.Bucket) *Server { 41 s := &Server{ 42 Name: name, 43 url: url, 44 store: store, 45 registry: registry, 46 peerServer: peerServer, 47 metrics: mb, 48 } 49 50 return s 51 } 52 53 func (s *Server) EnableTracing() { 54 s.trace = true 55 } 56 57 // The current state of the server in the cluster. 58 func (s *Server) State() string { 59 return s.peerServer.RaftServer().State() 60 } 61 62 // The node name of the leader in the cluster. 63 func (s *Server) Leader() string { 64 return s.peerServer.RaftServer().Leader() 65 } 66 67 // The current Raft committed index. 68 func (s *Server) CommitIndex() uint64 { 69 return s.peerServer.RaftServer().CommitIndex() 70 } 71 72 // The current Raft term. 73 func (s *Server) Term() uint64 { 74 return s.peerServer.RaftServer().Term() 75 } 76 77 // The server URL. 78 func (s *Server) URL() string { 79 return s.url 80 } 81 82 // PeerHost retrieves the host part of Peer URL for a given node name. 83 func (s *Server) PeerHost(name string) (string, bool) { 84 return s.registry.PeerHost(name) 85 } 86 87 // Retrives the Peer URL for a given node name. 88 func (s *Server) PeerURL(name string) (string, bool) { 89 return s.registry.PeerURL(name) 90 } 91 92 // ClientURL retrieves the Client URL for a given node name. 93 func (s *Server) ClientURL(name string) (string, bool) { 94 return s.registry.ClientURL(name) 95 } 96 97 // Returns a reference to the Store. 98 func (s *Server) Store() store.Store { 99 return s.store 100 } 101 102 func (s *Server) installV1(r *mux.Router) { 103 s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET", "HEAD") 104 s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT") 105 s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE") 106 s.handleFuncV1(r, "/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "HEAD", "POST") 107 s.handleFunc(r, "/v1/leader", s.GetLeaderHandler).Methods("GET", "HEAD") 108 s.handleFunc(r, "/v1/machines", s.GetPeersHandler).Methods("GET", "HEAD") 109 s.handleFunc(r, "/v1/peers", s.GetPeersHandler).Methods("GET", "HEAD") 110 s.handleFunc(r, "/v1/stats/self", s.GetStatsHandler).Methods("GET", "HEAD") 111 s.handleFunc(r, "/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET", "HEAD") 112 s.handleFunc(r, "/v1/stats/store", s.GetStoreStatsHandler).Methods("GET", "HEAD") 113 } 114 115 func (s *Server) installV2(r *mux.Router) { 116 r2 := mux.NewRouter() 117 r.PathPrefix("/v2").Handler(ehttp.NewLowerQueryParamsHandler(r2)) 118 119 s.handleFuncV2(r2, "/v2/keys/{key:.*}", v2.GetHandler).Methods("GET", "HEAD") 120 s.handleFuncV2(r2, "/v2/keys/{key:.*}", v2.PostHandler).Methods("POST") 121 s.handleFuncV2(r2, "/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT") 122 s.handleFuncV2(r2, "/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE") 123 s.handleFunc(r2, "/v2/leader", s.GetLeaderHandler).Methods("GET", "HEAD") 124 s.handleFunc(r2, "/v2/machines", s.GetPeersHandler).Methods("GET", "HEAD") 125 s.handleFunc(r2, "/v2/peers", s.GetPeersHandler).Methods("GET", "HEAD") 126 s.handleFunc(r2, "/v2/stats/self", s.GetStatsHandler).Methods("GET", "HEAD") 127 s.handleFunc(r2, "/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET", "HEAD") 128 s.handleFunc(r2, "/v2/stats/store", s.GetStoreStatsHandler).Methods("GET", "HEAD") 129 s.handleFunc(r2, "/v2/speedTest", s.SpeedTestHandler).Methods("GET", "HEAD") 130 } 131 132 func (s *Server) installMod(r *mux.Router) { 133 r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.URL()))) 134 } 135 136 func (s *Server) installDebug(r *mux.Router) { 137 s.handleFunc(r, "/debug/metrics", s.GetMetricsHandler).Methods("GET", "HEAD") 138 r.HandleFunc("/debug/pprof", pprof.Index) 139 r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 140 r.HandleFunc("/debug/pprof/profile", pprof.Profile) 141 r.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 142 r.HandleFunc("/debug/pprof/{name}", pprof.Index) 143 } 144 145 // Adds a v1 server handler to the router. 146 func (s *Server) handleFuncV1(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route { 147 return s.handleFunc(r, path, func(w http.ResponseWriter, req *http.Request) error { 148 return f(w, req, s) 149 }) 150 } 151 152 // Adds a v2 server handler to the router. 153 func (s *Server) handleFuncV2(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route { 154 return s.handleFunc(r, path, func(w http.ResponseWriter, req *http.Request) error { 155 return f(w, req, s) 156 }) 157 } 158 159 type HEADResponseWriter struct { 160 http.ResponseWriter 161 } 162 163 func (w *HEADResponseWriter) Write([]byte) (int, error) { 164 return 0, nil 165 } 166 167 // Adds a server handler to the router. 168 func (s *Server) handleFunc(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route { 169 170 // Wrap the standard HandleFunc interface to pass in the server reference. 171 return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { 172 if req.Method == "HEAD" { 173 w = &HEADResponseWriter{w} 174 } 175 176 // Log request. 177 log.Debugf("[recv] %s %s %s [%s]", req.Method, s.URL(), req.URL.Path, req.RemoteAddr) 178 179 // Forward request along if the server is a standby. 180 if s.peerServer.Mode() == StandbyMode { 181 if s.peerServer.standbyClientURL == "" { 182 w.Header().Set("Content-Type", "application/json") 183 etcdErr.NewError(402, "", 0).Write(w) 184 return 185 } 186 uhttp.Redirect(s.peerServer.standbyClientURL, w, req) 187 return 188 } 189 190 // Execute handler function and return error if necessary. 191 if err := f(w, req); err != nil { 192 if etcdErr, ok := err.(*etcdErr.Error); ok { 193 log.Debug("Return error: ", (*etcdErr).Error()) 194 w.Header().Set("Content-Type", "application/json") 195 etcdErr.Write(w) 196 } else { 197 http.Error(w, err.Error(), http.StatusInternalServerError) 198 } 199 } 200 }) 201 } 202 203 func (s *Server) HTTPHandler() http.Handler { 204 router := mux.NewRouter() 205 206 // Install the routes. 207 s.handleFunc(router, "/version", s.GetVersionHandler).Methods("GET") 208 s.installV1(router) 209 s.installV2(router) 210 // Mod is deprecated temporariy due to its unstable state. 211 // It would be added back later. 212 // s.installMod(router) 213 214 if s.trace { 215 s.installDebug(router) 216 } 217 218 return router 219 } 220 221 // Dispatch command to the current leader 222 func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error { 223 ps := s.peerServer 224 if ps.raftServer.State() == raft.Leader { 225 result, err := ps.raftServer.Do(c) 226 if err != nil { 227 return err 228 } 229 230 if result == nil { 231 return etcdErr.NewError(300, "Empty result from raft", s.Store().Index()) 232 } 233 234 w.Header().Set("X-Leader-Client-URL", s.url) 235 w.Header().Set("X-Leader-Peer-URL", ps.Config.URL) 236 237 // response for raft related commands[join/remove] 238 if b, ok := result.([]byte); ok { 239 w.WriteHeader(http.StatusOK) 240 w.Write(b) 241 return nil 242 } 243 244 var b []byte 245 if strings.HasPrefix(req.URL.Path, "/v1") { 246 b, _ = json.Marshal(result.(*store.Event).Response(0)) 247 w.WriteHeader(http.StatusOK) 248 } else { 249 e, _ := result.(*store.Event) 250 b, _ = json.Marshal(e) 251 252 w.Header().Set("Content-Type", "application/json") 253 // etcd index should be the same as the event index 254 // which is also the last modified index of the node 255 w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index())) 256 w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex())) 257 w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term())) 258 259 if e.IsCreated() { 260 w.WriteHeader(http.StatusCreated) 261 } else { 262 w.WriteHeader(http.StatusOK) 263 } 264 } 265 266 w.Write(b) 267 268 return nil 269 270 } 271 272 leader := ps.raftServer.Leader() 273 if leader == "" { 274 return etcdErr.NewError(300, "", s.Store().Index()) 275 } 276 277 var url string 278 switch c.(type) { 279 case *JoinCommandV1, *RemoveCommandV1, 280 *JoinCommandV2, *RemoveCommandV2, 281 *SetClusterConfigCommand: 282 url, _ = ps.registry.PeerURL(leader) 283 default: 284 url, _ = ps.registry.ClientURL(leader) 285 } 286 287 uhttp.Redirect(url, w, req) 288 289 return nil 290 } 291 292 // Handler to return the current version of etcd. 293 func (s *Server) GetVersionHandler(w http.ResponseWriter, req *http.Request) error { 294 w.WriteHeader(http.StatusOK) 295 fmt.Fprintf(w, "etcd %s", ReleaseVersion) 296 return nil 297 } 298 299 // Handler to return the current leader's raft address 300 func (s *Server) GetLeaderHandler(w http.ResponseWriter, req *http.Request) error { 301 leader := s.peerServer.RaftServer().Leader() 302 if leader == "" { 303 return etcdErr.NewError(etcdErr.EcodeLeaderElect, "", s.Store().Index()) 304 } 305 w.WriteHeader(http.StatusOK) 306 url, _ := s.registry.PeerURL(leader) 307 w.Write([]byte(url)) 308 return nil 309 } 310 311 // Handler to return all the known peers in the current cluster. 312 func (s *Server) GetPeersHandler(w http.ResponseWriter, req *http.Request) error { 313 peers := s.registry.ClientURLs(s.peerServer.RaftServer().Leader(), s.Name) 314 w.WriteHeader(http.StatusOK) 315 w.Write([]byte(strings.Join(peers, ", "))) 316 return nil 317 } 318 319 // Retrieves stats on the Raft server. 320 func (s *Server) GetStatsHandler(w http.ResponseWriter, req *http.Request) error { 321 w.Write(s.peerServer.Stats()) 322 return nil 323 } 324 325 // Retrieves stats on the leader. 326 func (s *Server) GetLeaderStatsHandler(w http.ResponseWriter, req *http.Request) error { 327 if s.peerServer.RaftServer().State() == raft.Leader { 328 w.Write(s.peerServer.PeerStats()) 329 return nil 330 } 331 332 leader := s.peerServer.RaftServer().Leader() 333 if leader == "" { 334 return etcdErr.NewError(300, "", s.Store().Index()) 335 } 336 hostname, _ := s.registry.ClientURL(leader) 337 uhttp.Redirect(hostname, w, req) 338 return nil 339 } 340 341 // Retrieves stats on the leader. 342 func (s *Server) GetStoreStatsHandler(w http.ResponseWriter, req *http.Request) error { 343 w.Write(s.store.JsonStats()) 344 return nil 345 } 346 347 // Executes a speed test to evaluate the performance of update replication. 348 func (s *Server) SpeedTestHandler(w http.ResponseWriter, req *http.Request) error { 349 count := 1000 350 c := make(chan bool, count) 351 for i := 0; i < count; i++ { 352 go func() { 353 for j := 0; j < 10; j++ { 354 c := s.Store().CommandFactory().CreateSetCommand("foo", false, "bar", time.Unix(0, 0)) 355 s.peerServer.RaftServer().Do(c) 356 } 357 c <- true 358 }() 359 } 360 361 for i := 0; i < count; i++ { 362 <-c 363 } 364 365 w.WriteHeader(http.StatusOK) 366 w.Write([]byte("speed test success")) 367 return nil 368 } 369 370 // Retrieves metrics from bucket 371 func (s *Server) GetMetricsHandler(w http.ResponseWriter, req *http.Request) error { 372 (*s.metrics).Dump(w) 373 return nil 374 }