github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/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") 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", "POST") 107 s.handleFunc(r, "/v1/leader", s.GetLeaderHandler).Methods("GET") 108 s.handleFunc(r, "/v1/machines", s.GetPeersHandler).Methods("GET") 109 s.handleFunc(r, "/v1/peers", s.GetPeersHandler).Methods("GET") 110 s.handleFunc(r, "/v1/stats/self", s.GetStatsHandler).Methods("GET") 111 s.handleFunc(r, "/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET") 112 s.handleFunc(r, "/v1/stats/store", s.GetStoreStatsHandler).Methods("GET") 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") 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") 124 s.handleFunc(r2, "/v2/machines", s.GetPeersHandler).Methods("GET") 125 s.handleFunc(r2, "/v2/peers", s.GetPeersHandler).Methods("GET") 126 s.handleFunc(r2, "/v2/stats/self", s.GetStatsHandler).Methods("GET") 127 s.handleFunc(r2, "/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET") 128 s.handleFunc(r2, "/v2/stats/store", s.GetStoreStatsHandler).Methods("GET") 129 s.handleFunc(r2, "/v2/speedTest", s.SpeedTestHandler).Methods("GET") 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") 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 // Adds a server handler to the router. 160 func (s *Server) handleFunc(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route { 161 162 // Wrap the standard HandleFunc interface to pass in the server reference. 163 return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { 164 // Log request. 165 log.Debugf("[recv] %s %s %s [%s]", req.Method, s.URL(), req.URL.Path, req.RemoteAddr) 166 167 // Forward request along if the server is a proxy. 168 if s.peerServer.Mode() == ProxyMode { 169 if s.peerServer.proxyClientURL == "" { 170 w.Header().Set("Content-Type", "application/json") 171 etcdErr.NewError(402, "", 0).Write(w) 172 return 173 } 174 uhttp.Redirect(s.peerServer.proxyClientURL, w, req) 175 return 176 } 177 178 // Execute handler function and return error if necessary. 179 if err := f(w, req); err != nil { 180 if etcdErr, ok := err.(*etcdErr.Error); ok { 181 log.Debug("Return error: ", (*etcdErr).Error()) 182 w.Header().Set("Content-Type", "application/json") 183 etcdErr.Write(w) 184 } else { 185 http.Error(w, err.Error(), http.StatusInternalServerError) 186 } 187 } 188 }) 189 } 190 191 func (s *Server) HTTPHandler() http.Handler { 192 router := mux.NewRouter() 193 194 // Install the routes. 195 s.handleFunc(router, "/version", s.GetVersionHandler).Methods("GET") 196 s.installV1(router) 197 s.installV2(router) 198 s.installMod(router) 199 200 if s.trace { 201 s.installDebug(router) 202 } 203 204 return router 205 } 206 207 // Dispatch command to the current leader 208 func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error { 209 ps := s.peerServer 210 if ps.raftServer.State() == raft.Leader { 211 result, err := ps.raftServer.Do(c) 212 if err != nil { 213 return err 214 } 215 216 if result == nil { 217 return etcdErr.NewError(300, "Empty result from raft", s.Store().Index()) 218 } 219 220 w.Header().Set("X-Leader-Client-URL", s.url) 221 w.Header().Set("X-Leader-Peer-URL", ps.Config.URL) 222 223 // response for raft related commands[join/remove] 224 if b, ok := result.([]byte); ok { 225 w.WriteHeader(http.StatusOK) 226 w.Write(b) 227 return nil 228 } 229 230 var b []byte 231 if strings.HasPrefix(req.URL.Path, "/v1") { 232 b, _ = json.Marshal(result.(*store.Event).Response(0)) 233 w.WriteHeader(http.StatusOK) 234 } else { 235 e, _ := result.(*store.Event) 236 b, _ = json.Marshal(e) 237 238 w.Header().Set("Content-Type", "application/json") 239 // etcd index should be the same as the event index 240 // which is also the last modified index of the node 241 w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index())) 242 w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex())) 243 w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term())) 244 245 if e.IsCreated() { 246 w.WriteHeader(http.StatusCreated) 247 } else { 248 w.WriteHeader(http.StatusOK) 249 } 250 } 251 252 w.Write(b) 253 254 return nil 255 256 } 257 258 leader := ps.raftServer.Leader() 259 if leader == "" { 260 return etcdErr.NewError(300, "", s.Store().Index()) 261 } 262 263 var url string 264 switch c.(type) { 265 case *JoinCommandV1, *RemoveCommandV1: 266 url, _ = ps.registry.PeerURL(leader) 267 case *JoinCommandV2, *RemoveCommandV2: 268 url, _ = ps.registry.PeerURL(leader) 269 default: 270 url, _ = ps.registry.ClientURL(leader) 271 } 272 273 uhttp.Redirect(url, w, req) 274 275 return nil 276 } 277 278 // Handler to return the current version of etcd. 279 func (s *Server) GetVersionHandler(w http.ResponseWriter, req *http.Request) error { 280 w.WriteHeader(http.StatusOK) 281 fmt.Fprintf(w, "etcd %s", ReleaseVersion) 282 return nil 283 } 284 285 // Handler to return the current leader's raft address 286 func (s *Server) GetLeaderHandler(w http.ResponseWriter, req *http.Request) error { 287 leader := s.peerServer.RaftServer().Leader() 288 if leader == "" { 289 return etcdErr.NewError(etcdErr.EcodeLeaderElect, "", s.Store().Index()) 290 } 291 w.WriteHeader(http.StatusOK) 292 url, _ := s.registry.PeerURL(leader) 293 w.Write([]byte(url)) 294 return nil 295 } 296 297 // Handler to return all the known peers in the current cluster. 298 func (s *Server) GetPeersHandler(w http.ResponseWriter, req *http.Request) error { 299 peers := s.registry.ClientURLs(s.peerServer.RaftServer().Leader(), s.Name) 300 w.WriteHeader(http.StatusOK) 301 w.Write([]byte(strings.Join(peers, ", "))) 302 return nil 303 } 304 305 // Retrieves stats on the Raft server. 306 func (s *Server) GetStatsHandler(w http.ResponseWriter, req *http.Request) error { 307 w.Write(s.peerServer.Stats()) 308 return nil 309 } 310 311 // Retrieves stats on the leader. 312 func (s *Server) GetLeaderStatsHandler(w http.ResponseWriter, req *http.Request) error { 313 if s.peerServer.RaftServer().State() == raft.Leader { 314 w.Write(s.peerServer.PeerStats()) 315 return nil 316 } 317 318 leader := s.peerServer.RaftServer().Leader() 319 if leader == "" { 320 return etcdErr.NewError(300, "", s.Store().Index()) 321 } 322 hostname, _ := s.registry.ClientURL(leader) 323 uhttp.Redirect(hostname, w, req) 324 return nil 325 } 326 327 // Retrieves stats on the leader. 328 func (s *Server) GetStoreStatsHandler(w http.ResponseWriter, req *http.Request) error { 329 w.Write(s.store.JsonStats()) 330 return nil 331 } 332 333 // Executes a speed test to evaluate the performance of update replication. 334 func (s *Server) SpeedTestHandler(w http.ResponseWriter, req *http.Request) error { 335 count := 1000 336 c := make(chan bool, count) 337 for i := 0; i < count; i++ { 338 go func() { 339 for j := 0; j < 10; j++ { 340 c := s.Store().CommandFactory().CreateSetCommand("foo", false, "bar", time.Unix(0, 0)) 341 s.peerServer.RaftServer().Do(c) 342 } 343 c <- true 344 }() 345 } 346 347 for i := 0; i < count; i++ { 348 <-c 349 } 350 351 w.WriteHeader(http.StatusOK) 352 w.Write([]byte("speed test success")) 353 return nil 354 } 355 356 // Retrieves metrics from bucket 357 func (s *Server) GetMetricsHandler(w http.ResponseWriter, req *http.Request) error { 358 (*s.metrics).Dump(w) 359 return nil 360 }