Browse Source

server: add QuorumGet

Yicheng Qin 11 years ago
parent
commit
cd4b35c841
4 changed files with 117 additions and 0 deletions
  1. 2 0
      etcd/v2_apply.go
  2. 15 0
      etcd/v2_http_get.go
  3. 94 0
      etcd/v2_http_kv_test.go
  4. 6 0
      etcd/v2_store.go

+ 2 - 0
etcd/v2_apply.go

@@ -49,6 +49,8 @@ func (p *participant) v2apply(index int64, ent raft.Entry) {
 		e, err = p.Store.CompareAndDelete(cmd.Key, cmd.PrevValue, cmd.PrevIndex)
 	case "cas":
 		e, err = p.Store.CompareAndSwap(cmd.Key, cmd.PrevValue, cmd.PrevIndex, cmd.Value, cmd.Time)
+	case "get":
+		e, err = p.Store.Get(cmd.Key, cmd.Recursive, cmd.Sorted)
 	case "sync":
 		p.Store.DeleteExpiredKeys(cmd.Time)
 		return

+ 15 - 0
etcd/v2_http_get.go

@@ -32,6 +32,9 @@ func (p *participant) GetHandler(w http.ResponseWriter, req *http.Request) error
 	sort := (req.FormValue("sorted") == "true")
 	waitIndex := req.FormValue("waitIndex")
 	stream := (req.FormValue("stream") == "true")
+	if req.FormValue("quorum") == "true" {
+		return p.handleQuorumGet(key, recursive, sort, w, req)
+	}
 	if req.FormValue("wait") == "true" {
 		return p.handleWatch(key, recursive, stream, waitIndex, w, req)
 	}
@@ -120,6 +123,18 @@ func (p *participant) handleGet(key string, recursive, sort bool, w http.Respons
 	return nil
 }
 
+func (p *participant) handleQuorumGet(key string, recursive, sort bool, w http.ResponseWriter, req *http.Request) error {
+	if req.Method == "HEAD" {
+		return fmt.Errorf("not support HEAD")
+	}
+	event, err := p.Get(key, recursive, sort)
+	if err != nil {
+		return err
+	}
+	p.handleRet(w, event)
+	return nil
+}
+
 func (p *participant) writeHeaders(w http.ResponseWriter) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Add("X-Etcd-Index", fmt.Sprint(p.Store.Index()))

+ 94 - 0
etcd/v2_http_kv_test.go

@@ -667,6 +667,100 @@ func TestV2Get(t *testing.T) {
 	afterTest(t)
 }
 
+func TestV2QuorumGet(t *testing.T) {
+	es, hs := buildCluster(1, false)
+	u := hs[0].URL
+	tc := NewTestClient()
+
+	v := url.Values{}
+	v.Set("value", "XXX")
+	resp, err := tc.PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo/bar/zar?quorum=true"), v)
+	if err != nil {
+		t.Error(err)
+	}
+	resp.Body.Close()
+
+	tests := []struct {
+		relativeURL string
+		wStatus     int
+		w           map[string]interface{}
+	}{
+		{
+			"/v2/keys/foo/bar/zar",
+			http.StatusOK,
+			map[string]interface{}{
+				"node": map[string]interface{}{
+					"key":   "/foo/bar/zar",
+					"value": "XXX",
+				},
+				"action": "get",
+			},
+		},
+		{
+			"/v2/keys/foo",
+			http.StatusOK,
+			map[string]interface{}{
+				"node": map[string]interface{}{
+					"key": "/foo",
+					"dir": true,
+					"nodes": []interface{}{
+						map[string]interface{}{
+							"key":           "/foo/bar",
+							"dir":           true,
+							"createdIndex":  float64(2),
+							"modifiedIndex": float64(2),
+						},
+					},
+				},
+				"action": "get",
+			},
+		},
+		{
+			"/v2/keys/foo?recursive=true",
+			http.StatusOK,
+			map[string]interface{}{
+				"node": map[string]interface{}{
+					"key": "/foo",
+					"dir": true,
+					"nodes": []interface{}{
+						map[string]interface{}{
+							"key":           "/foo/bar",
+							"dir":           true,
+							"createdIndex":  float64(2),
+							"modifiedIndex": float64(2),
+							"nodes": []interface{}{
+								map[string]interface{}{
+									"key":           "/foo/bar/zar",
+									"value":         "XXX",
+									"createdIndex":  float64(2),
+									"modifiedIndex": float64(2),
+								},
+							},
+						},
+					},
+				},
+				"action": "get",
+			},
+		},
+	}
+
+	for i, tt := range tests {
+		resp, _ := tc.Get(fmt.Sprintf("%s%s", u, tt.relativeURL))
+		if resp.StatusCode != tt.wStatus {
+			t.Errorf("#%d: status = %d, want %d", i, resp.StatusCode, tt.wStatus)
+		}
+		if resp.Header.Get("Content-Type") != "application/json" {
+			t.Errorf("#%d: header = %v, want %v", resp.Header.Get("Content-Type"), "application/json")
+		}
+		if err := checkBody(tc.ReadBodyJSON(resp), tt.w); err != nil {
+			t.Errorf("#%d: %v", i, err)
+		}
+	}
+
+	destoryCluster(t, es, hs)
+	afterTest(t)
+}
+
 func TestV2Watch(t *testing.T) {
 	es, hs := buildCluster(1, false)
 	u := hs[0].URL

+ 6 - 0
etcd/v2_store.go

@@ -33,6 +33,7 @@ type cmd struct {
 	Dir       bool
 	Recursive bool
 	Unique    bool
+	Sorted    bool
 	Time      time.Time
 }
 
@@ -66,6 +67,11 @@ func (p *participant) CAD(key string, prevValue string, prevIndex uint64) (*stor
 	return p.do(cad)
 }
 
+func (p *participant) Get(key string, recursive, sorted bool) (*store.Event, error) {
+	get := &cmd{Type: "get", Key: key, Recursive: recursive, Sorted: sorted}
+	return p.do(get)
+}
+
 func (p *participant) do(c *cmd) (*store.Event, error) {
 	data, err := json.Marshal(c)
 	if err != nil {