github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/server/peer_server_handlers.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"net/url"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/coreos/etcd/third_party/github.com/goraft/raft"
    11  	"github.com/coreos/etcd/third_party/github.com/gorilla/mux"
    12  
    13  	etcdErr "github.com/coreos/etcd/error"
    14  	"github.com/coreos/etcd/log"
    15  	uhttp "github.com/coreos/etcd/pkg/http"
    16  	"github.com/coreos/etcd/store"
    17  )
    18  
    19  // Get all the current logs
    20  func (ps *PeerServer) GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
    21  	log.Debugf("[recv] GET %s/log", ps.Config.URL)
    22  	w.Header().Set("Content-Type", "application/json")
    23  	w.WriteHeader(http.StatusOK)
    24  	json.NewEncoder(w).Encode(ps.raftServer.LogEntries())
    25  }
    26  
    27  // Response to vote request
    28  func (ps *PeerServer) VoteHttpHandler(w http.ResponseWriter, req *http.Request) {
    29  	rvreq := &raft.RequestVoteRequest{}
    30  
    31  	if _, err := rvreq.Decode(req.Body); err != nil {
    32  		http.Error(w, "", http.StatusBadRequest)
    33  		log.Warnf("[recv] BADREQUEST %s/vote [%v]", ps.Config.URL, err)
    34  		return
    35  	}
    36  
    37  	log.Debugf("[recv] POST %s/vote [%s]", ps.Config.URL, rvreq.CandidateName)
    38  
    39  	resp := ps.raftServer.RequestVote(rvreq)
    40  
    41  	if resp == nil {
    42  		log.Warn("[vote] Error: nil response")
    43  		http.Error(w, "", http.StatusInternalServerError)
    44  		return
    45  	}
    46  
    47  	if _, err := resp.Encode(w); err != nil {
    48  		log.Warn("[vote] Error: %v", err)
    49  		http.Error(w, "", http.StatusInternalServerError)
    50  		return
    51  	}
    52  }
    53  
    54  // Response to append entries request
    55  func (ps *PeerServer) AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) {
    56  	start := time.Now()
    57  	aereq := &raft.AppendEntriesRequest{}
    58  
    59  	if _, err := aereq.Decode(req.Body); err != nil {
    60  		http.Error(w, "", http.StatusBadRequest)
    61  		log.Warnf("[recv] BADREQUEST %s/log/append [%v]", ps.Config.URL, err)
    62  		return
    63  	}
    64  
    65  	log.Debugf("[recv] POST %s/log/append [%d]", ps.Config.URL, len(aereq.Entries))
    66  
    67  	ps.serverStats.RecvAppendReq(aereq.LeaderName, int(req.ContentLength))
    68  
    69  	resp := ps.raftServer.AppendEntries(aereq)
    70  
    71  	if resp == nil {
    72  		log.Warn("[ae] Error: nil response")
    73  		http.Error(w, "", http.StatusInternalServerError)
    74  		return
    75  	}
    76  
    77  	if !resp.Success() {
    78  		log.Debugf("[Append Entry] Step back")
    79  	}
    80  
    81  	if _, err := resp.Encode(w); err != nil {
    82  		log.Warn("[ae] Error: %v", err)
    83  		http.Error(w, "", http.StatusInternalServerError)
    84  		return
    85  	}
    86  
    87  	(*ps.metrics).Timer("timer.appendentries.handle").UpdateSince(start)
    88  }
    89  
    90  // Response to recover from snapshot request
    91  func (ps *PeerServer) SnapshotHttpHandler(w http.ResponseWriter, req *http.Request) {
    92  	ssreq := &raft.SnapshotRequest{}
    93  
    94  	if _, err := ssreq.Decode(req.Body); err != nil {
    95  		http.Error(w, "", http.StatusBadRequest)
    96  		log.Warnf("[recv] BADREQUEST %s/snapshot [%v]", ps.Config.URL, err)
    97  		return
    98  	}
    99  
   100  	log.Debugf("[recv] POST %s/snapshot", ps.Config.URL)
   101  
   102  	resp := ps.raftServer.RequestSnapshot(ssreq)
   103  
   104  	if resp == nil {
   105  		log.Warn("[ss] Error: nil response")
   106  		http.Error(w, "", http.StatusInternalServerError)
   107  		return
   108  	}
   109  
   110  	if _, err := resp.Encode(w); err != nil {
   111  		log.Warn("[ss] Error: %v", err)
   112  		http.Error(w, "", http.StatusInternalServerError)
   113  		return
   114  	}
   115  }
   116  
   117  // Response to recover from snapshot request
   118  func (ps *PeerServer) SnapshotRecoveryHttpHandler(w http.ResponseWriter, req *http.Request) {
   119  	ssrreq := &raft.SnapshotRecoveryRequest{}
   120  
   121  	if _, err := ssrreq.Decode(req.Body); err != nil {
   122  		http.Error(w, "", http.StatusBadRequest)
   123  		log.Warnf("[recv] BADREQUEST %s/snapshotRecovery [%v]", ps.Config.URL, err)
   124  		return
   125  	}
   126  
   127  	log.Debugf("[recv] POST %s/snapshotRecovery", ps.Config.URL)
   128  
   129  	resp := ps.raftServer.SnapshotRecoveryRequest(ssrreq)
   130  
   131  	if resp == nil {
   132  		log.Warn("[ssr] Error: nil response")
   133  		http.Error(w, "", http.StatusInternalServerError)
   134  		return
   135  	}
   136  
   137  	if _, err := resp.Encode(w); err != nil {
   138  		log.Warn("[ssr] Error: %v", err)
   139  		http.Error(w, "", http.StatusInternalServerError)
   140  		return
   141  	}
   142  }
   143  
   144  // Get the port that listening for etcd connecting of the server
   145  func (ps *PeerServer) EtcdURLHttpHandler(w http.ResponseWriter, req *http.Request) {
   146  	log.Debugf("[recv] Get %s/etcdURL/ ", ps.Config.URL)
   147  	w.WriteHeader(http.StatusOK)
   148  	w.Write([]byte(ps.server.URL()))
   149  }
   150  
   151  // Response to the join request
   152  func (ps *PeerServer) JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
   153  	command := &JoinCommandV1{}
   154  	if err := uhttp.DecodeJsonRequest(req, command); err != nil {
   155  		w.WriteHeader(http.StatusInternalServerError)
   156  		return
   157  	}
   158  
   159  	log.Debugf("Receive Join Request from %s", command.Name)
   160  	err := ps.server.Dispatch(command, w, req)
   161  
   162  	// Return status.
   163  	if err != nil {
   164  		if etcdErr, ok := err.(*etcdErr.Error); ok {
   165  			log.Debug("Return error: ", (*etcdErr).Error())
   166  			etcdErr.Write(w)
   167  		} else {
   168  			http.Error(w, err.Error(), http.StatusInternalServerError)
   169  		}
   170  	}
   171  }
   172  
   173  // Attempt to rejoin the cluster as a peer.
   174  func (ps *PeerServer) PromoteHttpHandler(w http.ResponseWriter, req *http.Request) {
   175  	log.Infof("%s attempting to promote in cluster: %s", ps.Config.Name, ps.standbyPeerURL)
   176  	url, err := url.Parse(ps.standbyPeerURL)
   177  	if err != nil {
   178  		w.WriteHeader(http.StatusInternalServerError)
   179  		return
   180  	}
   181  
   182  	err = ps.joinByPeer(ps.raftServer, url.Host, ps.Config.Scheme)
   183  	if err != nil {
   184  		log.Infof("%s error while promoting: %v", ps.Config.Name, err)
   185  		w.WriteHeader(http.StatusInternalServerError)
   186  		return
   187  	}
   188  	log.Infof("%s promoted in the cluster", ps.Config.Name)
   189  	w.WriteHeader(http.StatusOK)
   190  }
   191  
   192  // Response to remove request
   193  func (ps *PeerServer) RemoveHttpHandler(w http.ResponseWriter, req *http.Request) {
   194  	if req.Method != "DELETE" {
   195  		w.WriteHeader(http.StatusMethodNotAllowed)
   196  		return
   197  	}
   198  
   199  	vars := mux.Vars(req)
   200  	command := &RemoveCommandV1{
   201  		Name: vars["name"],
   202  	}
   203  
   204  	log.Debugf("[recv] Remove Request [%s]", command.Name)
   205  
   206  	ps.server.Dispatch(command, w, req)
   207  }
   208  
   209  // Returns a JSON-encoded cluster configuration.
   210  func (ps *PeerServer) getClusterConfigHttpHandler(w http.ResponseWriter, req *http.Request) {
   211  	json.NewEncoder(w).Encode(&ps.clusterConfig)
   212  }
   213  
   214  // Updates the cluster configuration.
   215  func (ps *PeerServer) setClusterConfigHttpHandler(w http.ResponseWriter, req *http.Request) {
   216  	// Decode map.
   217  	m := make(map[string]interface{})
   218  	if err := json.NewDecoder(req.Body).Decode(&m); err != nil {
   219  		http.Error(w, err.Error(), http.StatusInternalServerError)
   220  		return
   221  	}
   222  
   223  	// Copy config and update fields passed in.
   224  	config := &ClusterConfig{
   225  		ActiveSize:   ps.clusterConfig.ActiveSize,
   226  		PromoteDelay: ps.clusterConfig.PromoteDelay,
   227  	}
   228  	if activeSize, ok := m["activeSize"].(float64); ok {
   229  		config.ActiveSize = int(activeSize)
   230  	}
   231  	if promoteDelay, ok := m["promoteDelay"].(float64); ok {
   232  		config.PromoteDelay = int(promoteDelay)
   233  	}
   234  
   235  	// Issue command to update.
   236  	c := &SetClusterConfigCommand{Config: config}
   237  	log.Debugf("[recv] Update Cluster Config Request")
   238  	ps.server.Dispatch(c, w, req)
   239  
   240  	json.NewEncoder(w).Encode(&ps.clusterConfig)
   241  }
   242  
   243  // Retrieves a list of peers and standbys.
   244  func (ps *PeerServer) getMachinesHttpHandler(w http.ResponseWriter, req *http.Request) {
   245  	machines := make([]*machineMessage, 0)
   246  	for _, name := range ps.registry.Peers() {
   247  		machines = append(machines, ps.getMachineMessage(name))
   248  	}
   249  	for _, name := range ps.registry.Standbys() {
   250  		machines = append(machines, ps.getMachineMessage(name))
   251  	}
   252  	json.NewEncoder(w).Encode(&machines)
   253  }
   254  
   255  // Retrieve single peer or standby.
   256  func (ps *PeerServer) getMachineHttpHandler(w http.ResponseWriter, req *http.Request) {
   257  	vars := mux.Vars(req)
   258  	json.NewEncoder(w).Encode(ps.getMachineMessage(vars["name"]))
   259  }
   260  
   261  func (ps *PeerServer) getMachineMessage(name string) *machineMessage {
   262  	if ps.registry.PeerExists(name) {
   263  		clientURL, _ := ps.registry.ClientURL(name)
   264  		peerURL, _ := ps.registry.PeerURL(name)
   265  		return &machineMessage{
   266  			Name:      name,
   267  			Mode:      PeerMode,
   268  			ClientURL: clientURL,
   269  			PeerURL:   peerURL,
   270  		}
   271  	}
   272  
   273  	if ps.registry.StandbyExists(name) {
   274  		clientURL, _ := ps.registry.StandbyClientURL(name)
   275  		peerURL, _ := ps.registry.StandbyPeerURL(name)
   276  		return &machineMessage{
   277  			Name:      name,
   278  			Mode:      StandbyMode,
   279  			ClientURL: clientURL,
   280  			PeerURL:   peerURL,
   281  		}
   282  	}
   283  
   284  	return nil
   285  }
   286  
   287  // Adds a machine to the cluster.
   288  func (ps *PeerServer) addMachineHttpHandler(w http.ResponseWriter, req *http.Request) {
   289  	c := &JoinCommandV2{}
   290  	if err := uhttp.DecodeJsonRequest(req, c); err != nil {
   291  		w.WriteHeader(http.StatusInternalServerError)
   292  		return
   293  	}
   294  
   295  	log.Debugf("Receive Join Request (v2) from %s", c.Name)
   296  	if err := ps.server.Dispatch(c, w, req); err != nil {
   297  		if etcdErr, ok := err.(*etcdErr.Error); ok {
   298  			log.Debug("Return error: ", (*etcdErr).Error())
   299  			etcdErr.Write(w)
   300  		} else {
   301  			http.Error(w, err.Error(), http.StatusInternalServerError)
   302  		}
   303  	}
   304  }
   305  
   306  // Removes a machine from the cluster.
   307  func (ps *PeerServer) removeMachineHttpHandler(w http.ResponseWriter, req *http.Request) {
   308  	vars := mux.Vars(req)
   309  	c := &RemoveCommandV2{Name: vars["name"]}
   310  	log.Debugf("[recv] Remove Request [%s]", c.Name)
   311  	ps.server.Dispatch(c, w, req)
   312  }
   313  
   314  // Response to the name request
   315  func (ps *PeerServer) NameHttpHandler(w http.ResponseWriter, req *http.Request) {
   316  	log.Debugf("[recv] Get %s/name/ ", ps.Config.URL)
   317  	w.WriteHeader(http.StatusOK)
   318  	w.Write([]byte(ps.Config.Name))
   319  }
   320  
   321  // Response to the name request
   322  func (ps *PeerServer) VersionHttpHandler(w http.ResponseWriter, req *http.Request) {
   323  	log.Debugf("[recv] Get %s/version/ ", ps.Config.URL)
   324  	w.WriteHeader(http.StatusOK)
   325  	w.Write([]byte(strconv.Itoa(ps.store.Version())))
   326  }
   327  
   328  // Checks whether a given version is supported.
   329  func (ps *PeerServer) VersionCheckHttpHandler(w http.ResponseWriter, req *http.Request) {
   330  	log.Debugf("[recv] Get %s%s ", ps.Config.URL, req.URL.Path)
   331  	vars := mux.Vars(req)
   332  	version, _ := strconv.Atoi(vars["version"])
   333  	if version >= store.MinVersion() && version <= store.MaxVersion() {
   334  		w.WriteHeader(http.StatusOK)
   335  	} else {
   336  		w.WriteHeader(http.StatusForbidden)
   337  	}
   338  }
   339  
   340  // Upgrades the current store version to the next version.
   341  func (ps *PeerServer) UpgradeHttpHandler(w http.ResponseWriter, req *http.Request) {
   342  	log.Debugf("[recv] Get %s/version", ps.Config.URL)
   343  
   344  	// Check if upgrade is possible for all nodes.
   345  	if err := ps.Upgradable(); err != nil {
   346  		http.Error(w, err.Error(), http.StatusInternalServerError)
   347  		return
   348  	}
   349  
   350  	// Create an upgrade command from the current version.
   351  	c := ps.store.CommandFactory().CreateUpgradeCommand()
   352  	if err := ps.server.Dispatch(c, w, req); err != nil {
   353  		http.Error(w, err.Error(), http.StatusInternalServerError)
   354  		return
   355  	}
   356  
   357  	w.WriteHeader(http.StatusOK)
   358  }
   359  
   360  // machineMessage represents information about a peer or standby in the registry.
   361  type machineMessage struct {
   362  	Name      string `json:"name"`
   363  	Mode      Mode   `json:"mode"`
   364  	ClientURL string `json:"clientURL"`
   365  	PeerURL   string `json:"peerURL"`
   366  }