github.com/jpetazzo/etcd@v0.2.1-0.20140113055439-97f1363afac5/server/server.go (about)

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