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  }