Browse Source

etcdserver: support GET on admin endpoint

Xiang Li 11 years ago
parent
commit
8d6bb4a471
2 changed files with 95 additions and 1 deletions
  1. 30 1
      etcdserver/etcdhttp/http.go
  2. 65 0
      etcdserver/etcdhttp/http_test.go

+ 30 - 1
etcdserver/etcdhttp/http.go

@@ -149,13 +149,42 @@ func (h serverHandler) serveMachines(w http.ResponseWriter, r *http.Request) {
 }
 
 func (h serverHandler) serveAdminMembers(w http.ResponseWriter, r *http.Request) {
-	if !allowMethod(w, r.Method, "POST", "DELETE") {
+	if !allowMethod(w, r.Method, "GET", "POST", "DELETE") {
 		return
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultServerTimeout)
 	defer cancel()
 
 	switch r.Method {
+	case "GET":
+		idStr := strings.TrimPrefix(r.URL.Path, adminMembersPrefix)
+		if idStr == "" {
+			msmap := h.clusterStore.Get().Members()
+			ms := make([]*etcdserver.Member, 0, len(msmap))
+			for _, m := range msmap {
+				ms = append(ms, m)
+			}
+			w.Header().Set("Content-Type", "application/json")
+			if err := json.NewEncoder(w).Encode(ms); err != nil {
+				log.Printf("etcdhttp: %v", err)
+			}
+			return
+		}
+		id, err := strconv.ParseUint(idStr, 16, 64)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+		m := h.clusterStore.Get().FindID(id)
+		if m == nil {
+			http.Error(w, "member not found", http.StatusNotFound)
+			return
+		}
+		w.Header().Set("Content-Type", "application/json")
+		if err := json.NewEncoder(w).Encode(m); err != nil {
+			log.Printf("etcdhttp: %v", err)
+		}
+		return
 	case "POST":
 		ctype := r.Header.Get("Content-Type")
 		if ctype != "application/json" {

+ 65 - 0
etcdserver/etcdhttp/http_test.go

@@ -1485,6 +1485,16 @@ func TestServeAdminMembersFail(t *testing.T) {
 
 			http.StatusInternalServerError,
 		},
+		{
+			// etcdserver.GetMember bad id
+			&http.Request{
+				URL:    mustNewURL(t, path.Join(adminMembersPrefix, "badid")),
+				Method: "GET",
+			},
+			&errServer{},
+
+			http.StatusBadRequest,
+		},
 	}
 	for i, tt := range tests {
 		h := &serverHandler{
@@ -1527,6 +1537,61 @@ func (s *serverRecorder) RemoveMember(_ context.Context, id uint64) error {
 	return nil
 }
 
+func TestServeAdminMembersGet(t *testing.T) {
+	cluster := &fakeCluster{
+		members: []etcdserver.Member{
+			{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}},
+			{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}},
+		},
+	}
+	h := &serverHandler{
+		server:       &serverRecorder{},
+		clock:        clockwork.NewFakeClock(),
+		clusterStore: cluster,
+	}
+
+	msb, err := json.Marshal(cluster.members)
+	if err != nil {
+		t.Fatal(err)
+	}
+	wms := string(msb) + "\n"
+	mb, err := json.Marshal(cluster.members[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	wm := string(mb) + "\n"
+
+	tests := []struct {
+		path  string
+		wcode int
+		wct   string
+		wbody string
+	}{
+		{adminMembersPrefix, http.StatusOK, "application/json", wms},
+		{path.Join(adminMembersPrefix, "1"), http.StatusOK, "application/json", wm},
+		{path.Join(adminMembersPrefix, "100"), http.StatusNotFound, "text/plain; charset=utf-8", "member not found\n"},
+	}
+
+	for i, tt := range tests {
+		req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+		rw := httptest.NewRecorder()
+		h.serveAdminMembers(rw, req)
+
+		if rw.Code != tt.wcode {
+			t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+		}
+		if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
+			t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
+		}
+		if rw.Body.String() != tt.wbody {
+			t.Errorf("#%d: body = %s, want %s", i, rw.Body.String(), tt.wbody)
+		}
+	}
+}
+
 func TestServeAdminMembersPut(t *testing.T) {
 	u := mustNewURL(t, adminMembersPrefix)
 	raftAttr := etcdserver.RaftAttributes{PeerURLs: []string{"http://127.0.0.1:1"}}