Browse Source

etcdhttp: version endpoint also returns cluster version.

Xiang Li 10 years ago
parent
commit
6296054ff6

+ 2 - 0
etcdserver/cluster.go

@@ -53,6 +53,8 @@ type Cluster interface {
 	// IsIDRemoved checks whether the given ID has been removed from this
 	// IsIDRemoved checks whether the given ID has been removed from this
 	// cluster at some point in the past
 	// cluster at some point in the past
 	IsIDRemoved(id types.ID) bool
 	IsIDRemoved(id types.ID) bool
+	// ClusterVersion is the cluster-wide minimum major.minor version.
+	Version() *semver.Version
 }
 }
 
 
 // Cluster is a list of Members that belong to the same raft cluster
 // Cluster is a list of Members that belong to the same raft cluster

+ 23 - 3
etcdserver/etcdhttp/client.go

@@ -92,7 +92,7 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
 	mux := http.NewServeMux()
 	mux := http.NewServeMux()
 	mux.HandleFunc("/", http.NotFound)
 	mux.HandleFunc("/", http.NotFound)
 	mux.Handle(healthPath, healthHandler(server))
 	mux.Handle(healthPath, healthHandler(server))
-	mux.HandleFunc(versionPath, serveVersion)
+	mux.HandleFunc(versionPath, versionHandler(server.Cluster(), serveVersion))
 	mux.Handle(keysPrefix, kh)
 	mux.Handle(keysPrefix, kh)
 	mux.Handle(keysPrefix+"/", kh)
 	mux.Handle(keysPrefix+"/", kh)
 	mux.HandleFunc(statsPrefix+"/store", sh.serveStore)
 	mux.HandleFunc(statsPrefix+"/store", sh.serveStore)
@@ -357,11 +357,31 @@ func healthHandler(server *etcdserver.EtcdServer) http.HandlerFunc {
 	}
 	}
 }
 }
 
 
-func serveVersion(w http.ResponseWriter, r *http.Request) {
+func versionHandler(c etcdserver.Cluster, fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		v := c.Version()
+		if v != nil {
+			fn(w, r, v.String())
+		} else {
+			fn(w, r, "not_decided")
+		}
+	}
+}
+
+func serveVersion(w http.ResponseWriter, r *http.Request, clusterV string) {
 	if !allowMethod(w, r.Method, "GET") {
 	if !allowMethod(w, r.Method, "GET") {
 		return
 		return
 	}
 	}
-	w.Write(version.MarshalJSON())
+	vs := version.Versions{
+		Server:  version.Version,
+		Cluster: clusterV,
+	}
+
+	b, err := json.Marshal(&vs)
+	if err != nil {
+		log.Panicf("version: cannot marshal versions to json (%v)", err)
+	}
+	w.Write(b)
 }
 }
 
 
 // parseKeyRequest converts a received http.Request on keysPrefix to
 // parseKeyRequest converts a received http.Request on keysPrefix to

+ 10 - 3
etcdserver/etcdhttp/client_test.go

@@ -1326,11 +1326,18 @@ func TestServeVersion(t *testing.T) {
 		t.Fatalf("error creating request: %v", err)
 		t.Fatalf("error creating request: %v", err)
 	}
 	}
 	rw := httptest.NewRecorder()
 	rw := httptest.NewRecorder()
-	serveVersion(rw, req)
+	serveVersion(rw, req, "2.1.0")
 	if rw.Code != http.StatusOK {
 	if rw.Code != http.StatusOK {
 		t.Errorf("code=%d, want %d", rw.Code, http.StatusOK)
 		t.Errorf("code=%d, want %d", rw.Code, http.StatusOK)
 	}
 	}
-	w := version.MarshalJSON()
+	vs := version.Versions{
+		Server:  version.Version,
+		Cluster: "2.1.0",
+	}
+	w, err := json.Marshal(&vs)
+	if err != nil {
+		t.Fatal(err)
+	}
 	if g := rw.Body.String(); g != string(w) {
 	if g := rw.Body.String(); g != string(w) {
 		t.Fatalf("body = %q, want %q", g, string(w))
 		t.Fatalf("body = %q, want %q", g, string(w))
 	}
 	}
@@ -1345,7 +1352,7 @@ func TestServeVersionFails(t *testing.T) {
 			t.Fatalf("error creating request: %v", err)
 			t.Fatalf("error creating request: %v", err)
 		}
 		}
 		rw := httptest.NewRecorder()
 		rw := httptest.NewRecorder()
-		serveVersion(rw, req)
+		serveVersion(rw, req, "2.1.0")
 		if rw.Code != http.StatusMethodNotAllowed {
 		if rw.Code != http.StatusMethodNotAllowed {
 			t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
 			t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
 		}
 		}

+ 1 - 0
etcdserver/etcdhttp/http_test.go

@@ -48,6 +48,7 @@ func (c *fakeCluster) Members() []*etcdserver.Member {
 }
 }
 func (c *fakeCluster) Member(id types.ID) *etcdserver.Member { return c.members[uint64(id)] }
 func (c *fakeCluster) Member(id types.ID) *etcdserver.Member { return c.members[uint64(id)] }
 func (c *fakeCluster) IsIDRemoved(id types.ID) bool          { return false }
 func (c *fakeCluster) IsIDRemoved(id types.ID) bool          { return false }
+func (c *fakeCluster) Version() *semver.Version              { return nil }
 
 
 // errServer implements the etcd.Server interface for testing.
 // errServer implements the etcd.Server interface for testing.
 // It returns the given error from any Do/Process/AddMember/RemoveMember calls.
 // It returns the given error from any Do/Process/AddMember/RemoveMember calls.

+ 1 - 1
etcdserver/etcdhttp/peer.go

@@ -38,7 +38,7 @@ func NewPeerHandler(cluster etcdserver.Cluster, raftHandler http.Handler) http.H
 	mux.Handle(rafthttp.RaftPrefix, raftHandler)
 	mux.Handle(rafthttp.RaftPrefix, raftHandler)
 	mux.Handle(rafthttp.RaftPrefix+"/", raftHandler)
 	mux.Handle(rafthttp.RaftPrefix+"/", raftHandler)
 	mux.Handle(peerMembersPrefix, mh)
 	mux.Handle(peerMembersPrefix, mh)
-	mux.HandleFunc(versionPath, serveVersion)
+	mux.HandleFunc(versionPath, versionHandler(cluster, serveVersion))
 	return mux
 	return mux
 }
 }
 
 

+ 2 - 13
version/version.go

@@ -15,8 +15,6 @@
 package version
 package version
 
 
 import (
 import (
-	"encoding/json"
-	"log"
 	"os"
 	"os"
 	"path"
 	"path"
 
 
@@ -45,20 +43,11 @@ const (
 )
 )
 
 
 type Versions struct {
 type Versions struct {
-	Server string `json:"etcdserver"`
-	// TODO: etcdcluster version
+	Server  string `json:"etcdserver"`
+	Cluster string `json:"etcdcluster"`
 	// TODO: raft state machine version
 	// TODO: raft state machine version
 }
 }
 
 
-// MarshalJSON returns the JSON encoding of Versions struct.
-func MarshalJSON() []byte {
-	b, err := json.Marshal(Versions{Server: Version})
-	if err != nil {
-		log.Panicf("version: cannot marshal versions to json (%v)", err)
-	}
-	return b
-}
-
 func DetectDataDir(dirpath string) (DataDirVersion, error) {
 func DetectDataDir(dirpath string) (DataDirVersion, error) {
 	names, err := fileutil.ReadDir(dirpath)
 	names, err := fileutil.ReadDir(dirpath)
 	if err != nil {
 	if err != nil {