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  }