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