mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1365 from xiangli-cmu/admin_get
etcdserver: support GET on admin endpoint
This commit is contained in:
commit
16c9970a03
@ -25,6 +25,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -149,13 +150,43 @@ 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(SortableMemberSlice, 0, len(msmap))
|
||||
for _, m := range msmap {
|
||||
ms = append(ms, m)
|
||||
}
|
||||
sort.Sort(ms)
|
||||
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" {
|
||||
@ -551,3 +582,9 @@ func trimNodeExternPrefix(n *store.NodeExtern, prefix string) *store.NodeExtern
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type SortableMemberSlice []*etcdserver.Member
|
||||
|
||||
func (s SortableMemberSlice) Len() int { return len(s) }
|
||||
func (s SortableMemberSlice) Less(i, j int) bool { return s[i].ID < s[j].ID }
|
||||
func (s SortableMemberSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
@ -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"}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user