Browse Source

Intermediate commit.

Ben Johnson 12 years ago
parent
commit
bb9401544a
8 changed files with 122 additions and 72 deletions
  1. 3 0
      etcd.go
  2. 2 4
      server/join_command.go
  3. 101 13
      server/peer_server.go
  4. 1 1
      server/registry.go
  5. 0 1
      server/remove_command.go
  6. 8 2
      server/server.go
  7. 7 0
      server/util.go
  8. 0 51
      util.go

+ 3 - 0
etcd.go

@@ -185,12 +185,15 @@ func main() {
 	// Create peer server.
 	// Create peer server.
 	ps := NewPeerServer(info.Name, dirPath, info.RaftURL, info.RaftListenHost, &raftTLSConfig, &info.RaftTLS, registry)
 	ps := NewPeerServer(info.Name, dirPath, info.RaftURL, info.RaftListenHost, &raftTLSConfig, &info.RaftTLS, registry)
 	ps.MaxClusterSize = maxClusterSize
 	ps.MaxClusterSize = maxClusterSize
+	ps.RetryTimes = retryTimes
 
 
 	s := server.New(info.Name, info.EtcdURL, info.EtcdListenHost, &etcdTLSConfig, &info.EtcdTLS, r)
 	s := server.New(info.Name, info.EtcdURL, info.EtcdListenHost, &etcdTLSConfig, &info.EtcdTLS, r)
 	if err := e.AllowOrigins(cors); err != nil {
 	if err := e.AllowOrigins(cors); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 
 
+	ps.SetServer(server)
+
 	ps.ListenAndServe(snapshot)
 	ps.ListenAndServe(snapshot)
 	s.ListenAndServe()
 	s.ListenAndServe()
 }
 }

+ 2 - 4
server/join_command.go

@@ -19,16 +19,14 @@ type JoinCommand struct {
 	Name           string `json:"name"`
 	Name           string `json:"name"`
 	RaftURL        string `json:"raftURL"`
 	RaftURL        string `json:"raftURL"`
 	EtcdURL        string `json:"etcdURL"`
 	EtcdURL        string `json:"etcdURL"`
-	MaxClusterSize int    `json:"maxClusterSize"`
 }
 }
 
 
-func NewJoinCommand(version, name, raftUrl, etcdUrl string, maxClusterSize int) *JoinCommand {
+func NewJoinCommand(version, name, raftUrl, etcdUrl string) *JoinCommand {
 	return &JoinCommand{
 	return &JoinCommand{
 		RaftVersion:    version,
 		RaftVersion:    version,
 		Name:           name,
 		Name:           name,
 		RaftURL:        raftUrl,
 		RaftURL:        raftUrl,
 		EtcdURL:        etcdUrl,
 		EtcdURL:        etcdUrl,
-		MaxClusterSize: maxClusterSize,
 	}
 	}
 }
 }
 
 
@@ -51,7 +49,7 @@ func (c *JoinCommand) Apply(server *raft.Server) (interface{}, error) {
 	}
 	}
 
 
 	// Check machine number in the cluster
 	// Check machine number in the cluster
-	if ps.registry.Count() == c.MaxClusterSize {
+	if ps.registry.Count() == ps.MaxClusterSize {
 		log.Debug("Reject join request from ", c.Name)
 		log.Debug("Reject join request from ", c.Name)
 		return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMoreMachine, "", server.CommitIndex(), server.Term())
 		return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMoreMachine, "", server.CommitIndex(), server.Term())
 	}
 	}

+ 101 - 13
server/peer_server.go

@@ -19,6 +19,7 @@ import (
 
 
 type PeerServer struct {
 type PeerServer struct {
     *raft.Server
     *raft.Server
+    server         Server
     joinIndex      uint64
     joinIndex      uint64
     name           string
     name           string
     url            string
     url            string
@@ -31,6 +32,7 @@ type PeerServer struct {
     store          *store.Store
     store          *store.Store
     snapConf       *snapshotConf
     snapConf       *snapshotConf
     MaxClusterSize int
     MaxClusterSize int
+    RetryTimes     int
 }
 }
 
 
 // TODO: find a good policy to do snapshot
 // TODO: find a good policy to do snapshot
@@ -140,6 +142,16 @@ func (s *PeerServer) ListenAndServe(snapshot bool, cluster []string) {
 
 
 }
 }
 
 
+// Retrieves the underlying Raft server.
+func (s *PeerServer) RaftServer() *raft.Server {
+    return s.Server
+}
+
+// Associates the client server with the peer server.
+func (s *PeerServer) SetServer(server Server) {
+    s.server = server
+}
+
 // Get all the current logs
 // Get all the current logs
 func (s *PeerServer) GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
 func (s *PeerServer) GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
     log.Debugf("[recv] GET %s/log", s.url)
     log.Debugf("[recv] GET %s/log", s.url)
@@ -223,7 +235,7 @@ func (s *PeerServer) SnapshotRecoveryHttpHandler(w http.ResponseWriter, req *htt
 func (s *PeerServer) EtcdURLHttpHandler(w http.ResponseWriter, req *http.Request) {
 func (s *PeerServer) EtcdURLHttpHandler(w http.ResponseWriter, req *http.Request) {
     log.Debugf("[recv] Get %s/etcdURL/ ", s.url)
     log.Debugf("[recv] Get %s/etcdURL/ ", s.url)
     w.WriteHeader(http.StatusOK)
     w.WriteHeader(http.StatusOK)
-    w.Write([]byte(argInfo.EtcdURL))
+    w.Write([]byte(s.server.URL()))
 }
 }
 
 
 // Response to the join request
 // Response to the join request
@@ -271,13 +283,13 @@ func (s *PeerServer) RaftVersionHttpHandler(w http.ResponseWriter, req *http.Req
 }
 }
 
 
 func (s *PeerServer) dispatchRaftCommand(c raft.Command, w http.ResponseWriter, req *http.Request) error {
 func (s *PeerServer) dispatchRaftCommand(c raft.Command, w http.ResponseWriter, req *http.Request) error {
-    return s.dispatch(c, w, req, nameToRaftURL)
+    return s.dispatch(c, w, req)
 }
 }
 
 
 func (s *PeerServer) startAsLeader() {
 func (s *PeerServer) startAsLeader() {
     // leader need to join self as a peer
     // leader need to join self as a peer
     for {
     for {
-        _, err := s.Do(newJoinCommand(PeerVersion, s.Name(), s.url, e.url))
+        _, err := s.Do(NewJoinCommand(PeerVersion, s.Name(), s.url, s.server.URL()))
         if err == nil {
         if err == nil {
             break
             break
         }
         }
@@ -287,7 +299,7 @@ func (s *PeerServer) startAsLeader() {
 
 
 func (s *PeerServer) startAsFollower(cluster []string) {
 func (s *PeerServer) startAsFollower(cluster []string) {
     // start as a follower in a existing cluster
     // start as a follower in a existing cluster
-    for i := 0; i < retryTimes; i++ {
+    for i := 0; i < s.RetryTimes; i++ {
         ok := s.joinCluster(cluster)
         ok := s.joinCluster(cluster)
         if ok {
         if ok {
             return
             return
@@ -296,12 +308,12 @@ func (s *PeerServer) startAsFollower(cluster []string) {
         time.Sleep(time.Second * RetryInterval)
         time.Sleep(time.Second * RetryInterval)
     }
     }
 
 
-    fatalf("Cannot join the cluster via given machines after %x retries", retryTimes)
+    log.Fatalf("Cannot join the cluster via given machines after %x retries", s.RetryTimes)
 }
 }
 
 
 // Start to listen and response raft command
 // Start to listen and response raft command
 func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
 func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
-    infof("raft server [name %s, listen on %s, advertised url %s]", s.name, s.listenHost, s.url)
+    log.Infof("raft server [name %s, listen on %s, advertised url %s]", s.name, s.listenHost, s.url)
 
 
     raftMux := http.NewServeMux()
     raftMux := http.NewServeMux()
 
 
@@ -324,9 +336,9 @@ func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
     raftMux.HandleFunc("/etcdURL", s.EtcdURLHttpHandler)
     raftMux.HandleFunc("/etcdURL", s.EtcdURLHttpHandler)
 
 
     if scheme == "http" {
     if scheme == "http" {
-        fatal(server.ListenAndServe())
+        log.Fatal(server.ListenAndServe())
     } else {
     } else {
-        fatal(server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
+        log.Fatal(server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
     }
     }
 
 
 }
 }
@@ -336,11 +348,9 @@ func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
 // version clusters.
 // version clusters.
 func getVersion(t *transporter, versionURL url.URL) (string, error) {
 func getVersion(t *transporter, versionURL url.URL) (string, error) {
     resp, req, err := t.Get(versionURL.String())
     resp, req, err := t.Get(versionURL.String())
-
     if err != nil {
     if err != nil {
         return "", err
         return "", err
     }
     }
-
     defer resp.Body.Close()
     defer resp.Body.Close()
 
 
     t.CancelWhenTimeout(req)
     t.CancelWhenTimeout(req)
@@ -363,7 +373,7 @@ func (s *PeerServer) joinCluster(cluster []string) bool {
 
 
         } else {
         } else {
             if _, ok := err.(etcdErr.Error); ok {
             if _, ok := err.(etcdErr.Error); ok {
-                fatal(err)
+                log.Fatal(err)
             }
             }
 
 
             log.Debugf("cannot join to cluster via machine %s %s", machine, err)
             log.Debugf("cannot join to cluster via machine %s %s", machine, err)
@@ -392,7 +402,7 @@ func (s *PeerServer) joinByMachine(server *raft.Server, machine string, scheme s
         return fmt.Errorf("Unable to join: internal version mismatch, entire cluster must be running identical versions of etcd")
         return fmt.Errorf("Unable to join: internal version mismatch, entire cluster must be running identical versions of etcd")
     }
     }
 
 
-    json.NewEncoder(&b).Encode(newJoinCommand(PeerVersion, server.Name(), server.url, e.url))
+    json.NewEncoder(&b).Encode(NewJoinCommand(PeerVersion, server.Name(), s.url, s.server.URL()))
 
 
     joinURL := url.URL{Host: machine, Scheme: scheme, Path: "/join"}
     joinURL := url.URL{Host: machine, Scheme: scheme, Path: "/join"}
 
 
@@ -419,7 +429,7 @@ func (s *PeerServer) joinByMachine(server *raft.Server, machine string, scheme s
                 address := resp.Header.Get("Location")
                 address := resp.Header.Get("Location")
                 log.Debugf("Send Join Request to %s", address)
                 log.Debugf("Send Join Request to %s", address)
 
 
-                json.NewEncoder(&b).Encode(newJoinCommand(PeerVersion, server.Name(), server.url, e.url))
+                json.NewEncoder(&b).Encode(newJoinCommand(PeerVersion, server.Name(), s.url, s.server.URL()))
 
 
                 resp, req, err = t.Post(address, &b)
                 resp, req, err = t.Post(address, &b)
 
 
@@ -472,3 +482,81 @@ func (s *PeerServer) monitorSnapshot() {
         }
         }
     }
     }
 }
 }
+
+func (s *PeerServer) dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error {
+    if r.State() == raft.Leader {
+        if response, err := r.Do(c); err != nil {
+            return err
+        } else {
+            if response == nil {
+                return etcdErr.NewError(300, "Empty response from raft", store.UndefIndex, store.UndefTerm)
+            }
+
+            event, ok := response.(*store.Event)
+            if ok {
+                bytes, err := json.Marshal(event)
+                if err != nil {
+                    fmt.Println(err)
+                }
+
+                w.Header().Add("X-Etcd-Index", fmt.Sprint(event.Index))
+                w.Header().Add("X-Etcd-Term", fmt.Sprint(event.Term))
+                w.WriteHeader(http.StatusOK)
+                w.Write(bytes)
+
+                return nil
+            }
+
+            bytes, _ := response.([]byte)
+            w.WriteHeader(http.StatusOK)
+            w.Write(bytes)
+
+            return nil
+        }
+
+    } else {
+        leader := r.Leader()
+        // current no leader
+        if leader == "" {
+            return etcdErr.NewError(300, "", store.UndefIndex, store.UndefTerm)
+        }
+        url, _ := s.registry.PeerURL(leader)
+
+        redirect(url, w, req)
+
+        return nil
+    }
+}
+
+
+type errorHandler func(http.ResponseWriter, *http.Request) error
+
+// addCorsHeader parses the request Origin header and loops through the user
+// provided allowed origins and sets the Access-Control-Allow-Origin header if
+// there is a match.
+func addCorsHeader(w http.ResponseWriter, r *http.Request) {
+    val, ok := corsList["*"]
+    if val && ok {
+        w.Header().Add("Access-Control-Allow-Origin", "*")
+        return
+    }
+
+    requestOrigin := r.Header.Get("Origin")
+    val, ok = corsList[requestOrigin]
+    if val && ok {
+        w.Header().Add("Access-Control-Allow-Origin", requestOrigin)
+        return
+    }
+}
+
+func (fn errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+    addCorsHeader(w, r)
+    if e := fn(w, r); e != nil {
+        if etcdErr, ok := e.(*etcdErr.Error); ok {
+            debug("Return error: ", (*etcdErr).Error())
+            etcdErr.Write(w)
+        } else {
+            http.Error(w, e.Error(), http.StatusInternalServerError)
+        }
+    }
+}

+ 1 - 1
server/registry.go

@@ -159,7 +159,7 @@ func (r *Registry) load(name string) {
     }
     }
 
 
     // Create node.
     // Create node.
-    r.nodes[name] := &node{
+    r.nodes[name] = &node{
         url: m["etcd"][0],
         url: m["etcd"][0],
         peerURL: m["raft"][0],
         peerURL: m["raft"][0],
         peerVersion: m["raftVersion"][0],
         peerVersion: m["raftVersion"][0],

+ 0 - 1
server/remove_command.go

@@ -2,7 +2,6 @@ package server
 
 
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
-	"path"
 
 
 	"github.com/coreos/etcd/store"
 	"github.com/coreos/etcd/store"
 	"github.com/coreos/go-raft"
 	"github.com/coreos/go-raft"

+ 8 - 2
server/server.go

@@ -12,6 +12,7 @@ import (
 type Server interface {
 type Server interface {
 	CommitIndex() uint64
 	CommitIndex() uint64
 	Term() uint64
 	Term() uint64
+	URL() string
 	Dispatch(raft.Command, http.ResponseWriter, *http.Request)
 	Dispatch(raft.Command, http.ResponseWriter, *http.Request)
 }
 }
 
 
@@ -49,12 +50,17 @@ func New(name string, urlStr string, listenHost string, tlsConf *TLSConfig, tlsI
 
 
 // The current Raft committed index.
 // The current Raft committed index.
 func (s *server) CommitIndex() uint64 {
 func (s *server) CommitIndex() uint64 {
-	return c.raftServer.CommitIndex()
+	return s.raftServer.CommitIndex()
 }
 }
 
 
 // The current Raft term.
 // The current Raft term.
 func (s *server) Term() uint64 {
 func (s *server) Term() uint64 {
-	return c.raftServer.Term()
+	return s.raftServer.Term()
+}
+
+// The server URL.
+func (s *server) URL() string {
+	return s.url
 }
 }
 
 
 func (s *server) installV1() {
 func (s *server) installV1() {

+ 7 - 0
server/util.go

@@ -15,3 +15,10 @@ func decodeJsonRequest(req *http.Request, data interface{}) error {
     return nil
     return nil
 }
 }
 
 
+func redirect(hostname string, w http.ResponseWriter, req *http.Request) {
+    path := req.URL.Path
+    url := hostname + path
+    debugf("Redirect to %s", url)
+    http.Redirect(w, req, url, http.StatusTemporaryRedirect)
+}
+

+ 0 - 51
util.go

@@ -41,57 +41,6 @@ func durationToExpireTime(strDuration string) (time.Time, error) {
 // HTTP Utilities
 // HTTP Utilities
 //--------------------------------------
 //--------------------------------------
 
 
-func (r *raftServer) dispatch(c Command, w http.ResponseWriter, req *http.Request, toURL func(name string) (string, bool)) error {
-	if r.State() == raft.Leader {
-		if response, err := r.Do(c); err != nil {
-			return err
-		} else {
-			if response == nil {
-				return etcdErr.NewError(300, "Empty response from raft", store.UndefIndex, store.UndefTerm)
-			}
-
-			event, ok := response.(*store.Event)
-			if ok {
-				bytes, err := json.Marshal(event)
-				if err != nil {
-					fmt.Println(err)
-				}
-
-				w.Header().Add("X-Etcd-Index", fmt.Sprint(event.Index))
-				w.Header().Add("X-Etcd-Term", fmt.Sprint(event.Term))
-				w.WriteHeader(http.StatusOK)
-				w.Write(bytes)
-
-				return nil
-			}
-
-			bytes, _ := response.([]byte)
-			w.WriteHeader(http.StatusOK)
-			w.Write(bytes)
-
-			return nil
-		}
-
-	} else {
-		leader := r.Leader()
-		// current no leader
-		if leader == "" {
-			return etcdErr.NewError(300, "", store.UndefIndex, store.UndefTerm)
-		}
-		url, _ := toURL(leader)
-
-		redirect(url, w, req)
-
-		return nil
-	}
-}
-
-func redirect(hostname string, w http.ResponseWriter, req *http.Request) {
-	path := req.URL.Path
-	url := hostname + path
-	debugf("Redirect to %s", url)
-	http.Redirect(w, req, url, http.StatusTemporaryRedirect)
-}
 
 
 // sanitizeURL will cleanup a host string in the format hostname:port and
 // sanitizeURL will cleanup a host string in the format hostname:port and
 // attach a schema.
 // attach a schema.