Browse Source

etcd: add version handlers

Xiang Li 11 years ago
parent
commit
3fba10c8e6
7 changed files with 111 additions and 54 deletions
  1. 7 1
      etcd/etcd.go
  2. 60 0
      etcd/etcd_test.go
  3. 1 2
      etcd/participant.go
  4. 27 4
      etcd/raft_handler.go
  5. 14 0
      etcd/version.go
  6. 2 2
      store/command_factory.go
  7. 0 45
      tests/functional/version_check_test.go

+ 7 - 1
etcd/etcd.go

@@ -52,6 +52,7 @@ type Server struct {
 	mu      sync.Mutex
 	mu      sync.Mutex
 	stopc   chan struct{}
 	stopc   chan struct{}
 	log     *log.Logger
 	log     *log.Logger
+	http.Handler
 }
 }
 
 
 func New(c *config.Config) *Server {
 func New(c *config.Config) *Server {
@@ -88,6 +89,11 @@ func New(c *config.Config) *Server {
 
 
 		stopc: make(chan struct{}),
 		stopc: make(chan struct{}),
 	}
 	}
+	m := http.NewServeMux()
+	m.HandleFunc("/", s.requestHandler)
+	m.HandleFunc("/version", versionHandler)
+	s.Handler = m
+
 	log.Printf("id=%x server.new raftPubAddr=%s\n", s.id, s.raftPubAddr)
 	log.Printf("id=%x server.new raftPubAddr=%s\n", s.id, s.raftPubAddr)
 
 
 	return s
 	return s
@@ -118,7 +124,7 @@ func (s *Server) Stop() {
 	log.Printf("id=%x server.stop\n", s.id)
 	log.Printf("id=%x server.stop\n", s.id)
 }
 }
 
 
-func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (s *Server) requestHandler(w http.ResponseWriter, r *http.Request) {
 	switch s.mode.Get() {
 	switch s.mode.Get() {
 	case participantMode:
 	case participantMode:
 		s.p.ServeHTTP(w, r)
 		s.p.ServeHTTP(w, r)

+ 60 - 0
etcd/etcd_test.go

@@ -18,6 +18,7 @@ package etcd
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"io/ioutil"
 	"math/rand"
 	"math/rand"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
@@ -280,6 +281,65 @@ func TestBecomeStandby(t *testing.T) {
 	afterTest(t)
 	afterTest(t)
 }
 }
 
 
+func TestReleaseVersion(t *testing.T) {
+	es, hs := buildCluster(1, false)
+
+	resp, err := http.Get(hs[0].URL + "/version")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer resp.Body.Close()
+	g, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		t.Error(err)
+	}
+	gs := string(g)
+	w := fmt.Sprintf("etcd %s", releaseVersion)
+	if gs != w {
+		t.Errorf("version = %v, want %v", gs, w)
+	}
+
+	for i := range hs {
+		es[len(hs)-i-1].Stop()
+	}
+	for i := range hs {
+		hs[len(hs)-i-1].Close()
+	}
+}
+
+func TestVersionCheck(t *testing.T) {
+	es, hs := buildCluster(1, false)
+	u := hs[0].URL
+
+	currentVersion := 2
+	tests := []struct {
+		version int
+		wStatus int
+	}{
+		{currentVersion - 1, http.StatusForbidden},
+		{currentVersion, http.StatusOK},
+		{currentVersion + 1, http.StatusForbidden},
+	}
+
+	for i, tt := range tests {
+		resp, err := http.Get(fmt.Sprintf("%s/raft/version/%d/check", u, tt.version))
+		if err != nil {
+			t.Fatal(err)
+		}
+		resp.Body.Close()
+		if resp.StatusCode != tt.wStatus {
+			t.Fatal("#%d: status = %d, want %d", i, resp.StatusCode, tt.wStatus)
+		}
+	}
+
+	for i := range hs {
+		es[len(hs)-i-1].Stop()
+	}
+	for i := range hs {
+		hs[len(hs)-i-1].Close()
+	}
+}
+
 func buildCluster(number int, tls bool) ([]*Server, []*httptest.Server) {
 func buildCluster(number int, tls bool) ([]*Server, []*httptest.Server) {
 	bootstrapper := 0
 	bootstrapper := 0
 	es := make([]*Server, number)
 	es := make([]*Server, number)

+ 1 - 2
etcd/participant.go

@@ -101,13 +101,12 @@ func newParticipant(id int64, pubAddr string, raftPubAddr string, client *v2clie
 			result: make(map[wait]chan interface{}),
 			result: make(map[wait]chan interface{}),
 		},
 		},
 		Store: store.New(),
 		Store: store.New(),
-		rh:    newRaftHandler(peerHub),
 
 
 		stopc: make(chan struct{}),
 		stopc: make(chan struct{}),
 
 
 		ServeMux: http.NewServeMux(),
 		ServeMux: http.NewServeMux(),
 	}
 	}
-
+	p.rh = newRaftHandler(peerHub, p.Store.Version())
 	p.Handle(v2Prefix+"/", handlerErr(p.serveValue))
 	p.Handle(v2Prefix+"/", handlerErr(p.serveValue))
 	p.Handle(v2machinePrefix, handlerErr(p.serveMachines))
 	p.Handle(v2machinePrefix, handlerErr(p.serveMachines))
 	p.Handle(v2peersPrefix, handlerErr(p.serveMachines))
 	p.Handle(v2peersPrefix, handlerErr(p.serveMachines))

+ 27 - 4
etcd/raft_handler.go

@@ -18,12 +18,14 @@ package etcd
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"fmt"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
 	"strconv"
 	"strconv"
 	"sync"
 	"sync"
 
 
 	"github.com/coreos/etcd/raft"
 	"github.com/coreos/etcd/raft"
+	"github.com/coreos/etcd/store"
 )
 )
 
 
 const (
 const (
@@ -34,20 +36,24 @@ type raftHandler struct {
 	mu      sync.RWMutex
 	mu      sync.RWMutex
 	serving bool
 	serving bool
 
 
-	peerGetter peerGetter
+	peerGetter   peerGetter
+	storeVersion int
 
 
 	recv chan *raft.Message
 	recv chan *raft.Message
 	*http.ServeMux
 	*http.ServeMux
 }
 }
 
 
-func newRaftHandler(p peerGetter) *raftHandler {
+func newRaftHandler(p peerGetter, version int) *raftHandler {
 	h := &raftHandler{
 	h := &raftHandler{
-		recv:       make(chan *raft.Message, 512),
-		peerGetter: p,
+		recv:         make(chan *raft.Message, 512),
+		peerGetter:   p,
+		storeVersion: version,
 	}
 	}
 	h.ServeMux = http.NewServeMux()
 	h.ServeMux = http.NewServeMux()
 	h.ServeMux.HandleFunc(raftPrefix+"/cfg/", h.serveCfg)
 	h.ServeMux.HandleFunc(raftPrefix+"/cfg/", h.serveCfg)
 	h.ServeMux.HandleFunc(raftPrefix, h.serveRaft)
 	h.ServeMux.HandleFunc(raftPrefix, h.serveRaft)
+	h.ServeMux.HandleFunc(raftPrefix+"/version", h.serveVersion)
+	h.ServeMux.HandleFunc(raftPrefix+"/version/", h.serveVersionCheck)
 	return h
 	return h
 }
 }
 
 
@@ -110,3 +116,20 @@ func (h *raftHandler) serveCfg(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	http.Error(w, err.Error(), http.StatusNotFound)
 	http.Error(w, err.Error(), http.StatusNotFound)
 }
 }
+
+func (h *raftHandler) serveVersion(w http.ResponseWriter, req *http.Request) {
+	w.Write([]byte(strconv.Itoa(h.storeVersion)))
+}
+
+func (h *raftHandler) serveVersionCheck(w http.ResponseWriter, req *http.Request) {
+	var version int
+	n, err := fmt.Sscanf(req.URL.Path, raftPrefix+"/version/%d/check", &version)
+	if err != nil || n != 1 {
+		http.Error(w, "error version check format: "+req.URL.Path, http.StatusBadRequest)
+		return
+	}
+	if version < store.MinVersion() || version > store.MaxVersion() {
+		w.WriteHeader(http.StatusForbidden)
+		return
+	}
+}

+ 14 - 0
etcd/version.go

@@ -0,0 +1,14 @@
+package etcd
+
+import (
+	"fmt"
+	"net/http"
+)
+
+const (
+	releaseVersion = "0.5rc1+git"
+)
+
+func versionHandler(w http.ResponseWriter, req *http.Request) {
+	fmt.Fprintf(w, "etcd %s", releaseVersion)
+}

+ 2 - 2
store/command_factory.go

@@ -53,10 +53,10 @@ func GetCommandFactory(version int) CommandFactory {
 
 
 // MinVersion returns the minimum compatible store version.
 // MinVersion returns the minimum compatible store version.
 func MinVersion() int {
 func MinVersion() int {
-	return minVersion
+	return 2
 }
 }
 
 
 // MaxVersion returns the maximum compatible store version.
 // MaxVersion returns the maximum compatible store version.
 func MaxVersion() int {
 func MaxVersion() int {
-	return maxVersion
+	return 2
 }
 }

+ 0 - 45
tests/functional/version_check_test.go

@@ -1,45 +0,0 @@
-package test
-
-import (
-	"net/http"
-	"os"
-	"testing"
-	"time"
-)
-
-// Ensure that a node can reply to a version check appropriately.
-func TestVersionCheck(t *testing.T) {
-	procAttr := new(os.ProcAttr)
-	procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr}
-	args := []string{"etcd", "-name=node1", "-f", "-data-dir=/tmp/version_check"}
-
-	process, err := os.StartProcess(EtcdBinPath, args, procAttr)
-	if err != nil {
-		t.Fatal("start process failed:" + err.Error())
-		return
-	}
-	defer process.Kill()
-
-	time.Sleep(time.Second)
-
-	// Check a version too small.
-	resp, _ := http.Get("http://localhost:7001/version/1/check")
-	resp.Body.Close()
-	if resp.StatusCode != http.StatusForbidden {
-		t.Fatal("Invalid version check: ", resp.StatusCode)
-	}
-
-	// Check a version too large.
-	resp, _ = http.Get("http://localhost:7001/version/3/check")
-	resp.Body.Close()
-	if resp.StatusCode != http.StatusForbidden {
-		t.Fatal("Invalid version check: ", resp.StatusCode)
-	}
-
-	// Check a version that's just right.
-	resp, _ = http.Get("http://localhost:7001/version/2/check")
-	resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Fatal("Invalid version check: ", resp.StatusCode)
-	}
-}