Browse Source

client: explicitly carry API prefix around

Brian Waldon 11 years ago
parent
commit
7c1f4a9baf
4 changed files with 47 additions and 24 deletions
  1. 24 15
      client/keys.go
  2. 14 1
      client/keys_test.go
  3. 5 4
      client/members.go
  4. 4 4
      client/members_test.go

+ 24 - 15
client/keys.go

@@ -55,8 +55,6 @@ func newHTTPKeysAPIWithPrefix(tr *http.Transport, ep string, to time.Duration, p
 		return nil, err
 	}
 
-	u.Path = path.Join(u.Path, prefix)
-
 	c := &httpClient{
 		transport: tr,
 		endpoint:  *u,
@@ -65,6 +63,7 @@ func newHTTPKeysAPIWithPrefix(tr *http.Transport, ep string, to time.Duration, p
 
 	kAPI := httpKeysAPI{
 		client: c,
+		prefix: prefix,
 	}
 
 	return &kAPI, nil
@@ -102,12 +101,14 @@ func (n *Node) String() string {
 
 type httpKeysAPI struct {
 	client *httpClient
+	prefix string
 }
 
 func (k *httpKeysAPI) Create(key, val string, ttl time.Duration) (*Response, error) {
 	create := &createAction{
-		Key:   key,
-		Value: val,
+		Prefix: k.prefix,
+		Key:    key,
+		Value:  val,
 	}
 	if ttl >= 0 {
 		uttl := uint64(ttl.Seconds())
@@ -124,6 +125,7 @@ func (k *httpKeysAPI) Create(key, val string, ttl time.Duration) (*Response, err
 
 func (k *httpKeysAPI) Get(key string) (*Response, error) {
 	get := &getAction{
+		Prefix:    k.prefix,
 		Key:       key,
 		Recursive: false,
 	}
@@ -140,6 +142,7 @@ func (k *httpKeysAPI) Watch(key string, idx uint64) Watcher {
 	return &httpWatcher{
 		client: k.client,
 		nextWait: waitAction{
+			Prefix:    k.prefix,
 			Key:       key,
 			WaitIndex: idx,
 			Recursive: false,
@@ -151,6 +154,7 @@ func (k *httpKeysAPI) RecursiveWatch(key string, idx uint64) Watcher {
 	return &httpWatcher{
 		client: k.client,
 		nextWait: waitAction{
+			Prefix:    k.prefix,
 			Key:       key,
 			WaitIndex: idx,
 			Recursive: true,
@@ -179,21 +183,24 @@ func (hw *httpWatcher) Next() (*Response, error) {
 	return resp, nil
 }
 
-// v2KeysURL forms a URL representing the location of a key. The provided
-// endpoint must be the root of the etcd keys API. For example, a valid
-// endpoint probably has the path "/v2/keys".
-func v2KeysURL(ep url.URL, key string) *url.URL {
-	ep.Path = path.Join(ep.Path, key)
+// v2KeysURL forms a URL representing the location of a key.
+// The endpoint argument represents the base URL of an etcd
+// server. The prefix is the path needed to route from the
+// provided endpoint's path to the root of the keys API
+// (typically "/v2/keys").
+func v2KeysURL(ep url.URL, prefix, key string) *url.URL {
+	ep.Path = path.Join(ep.Path, prefix, key)
 	return &ep
 }
 
 type getAction struct {
+	Prefix    string
 	Key       string
 	Recursive bool
 }
 
 func (g *getAction) httpRequest(ep url.URL) *http.Request {
-	u := v2KeysURL(ep, g.Key)
+	u := v2KeysURL(ep, g.Prefix, g.Key)
 
 	params := u.Query()
 	params.Set("recursive", strconv.FormatBool(g.Recursive))
@@ -204,13 +211,14 @@ func (g *getAction) httpRequest(ep url.URL) *http.Request {
 }
 
 type waitAction struct {
+	Prefix    string
 	Key       string
 	WaitIndex uint64
 	Recursive bool
 }
 
 func (w *waitAction) httpRequest(ep url.URL) *http.Request {
-	u := v2KeysURL(ep, w.Key)
+	u := v2KeysURL(ep, w.Prefix, w.Key)
 
 	params := u.Query()
 	params.Set("wait", "true")
@@ -223,13 +231,14 @@ func (w *waitAction) httpRequest(ep url.URL) *http.Request {
 }
 
 type createAction struct {
-	Key   string
-	Value string
-	TTL   *uint64
+	Prefix string
+	Key    string
+	Value  string
+	TTL    *uint64
 }
 
 func (c *createAction) httpRequest(ep url.URL) *http.Request {
-	u := v2KeysURL(ep, c.Key)
+	u := v2KeysURL(ep, c.Prefix, c.Key)
 
 	params := u.Query()
 	params.Set("prevExist", "false")

+ 14 - 1
client/keys_test.go

@@ -29,12 +29,14 @@ import (
 func TestV2KeysURLHelper(t *testing.T) {
 	tests := []struct {
 		endpoint url.URL
+		prefix   string
 		key      string
 		want     url.URL
 	}{
 		// key is empty, no problem
 		{
 			endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
+			prefix:   "",
 			key:      "",
 			want:     url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
 		},
@@ -42,6 +44,7 @@ func TestV2KeysURLHelper(t *testing.T) {
 		// key is joined to path
 		{
 			endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
+			prefix:   "",
 			key:      "/foo/bar",
 			want:     url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys/foo/bar"},
 		},
@@ -49,6 +52,7 @@ func TestV2KeysURLHelper(t *testing.T) {
 		// key is joined to path when path is empty
 		{
 			endpoint: url.URL{Scheme: "http", Host: "example.com", Path: ""},
+			prefix:   "",
 			key:      "/foo/bar",
 			want:     url.URL{Scheme: "http", Host: "example.com", Path: "/foo/bar"},
 		},
@@ -56,6 +60,7 @@ func TestV2KeysURLHelper(t *testing.T) {
 		// Host field carries through with port
 		{
 			endpoint: url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
+			prefix:   "",
 			key:      "",
 			want:     url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
 		},
@@ -63,13 +68,21 @@ func TestV2KeysURLHelper(t *testing.T) {
 		// Scheme carries through
 		{
 			endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
+			prefix:   "",
 			key:      "",
 			want:     url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
 		},
+		// Prefix is applied
+		{
+			endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/foo"},
+			prefix:   "/bar",
+			key:      "/baz",
+			want:     url.URL{Scheme: "https", Host: "example.com", Path: "/foo/bar/baz"},
+		},
 	}
 
 	for i, tt := range tests {
-		got := v2KeysURL(tt.endpoint, tt.key)
+		got := v2KeysURL(tt.endpoint, tt.prefix, tt.key)
 		if tt.want != *got {
 			t.Errorf("#%d: want=%#v, got=%#v", i, tt.want, *got)
 		}

+ 5 - 4
client/members.go

@@ -39,8 +39,6 @@ func NewMembersAPI(tr *http.Transport, ep string, to time.Duration) (MembersAPI,
 		return nil, err
 	}
 
-	u.Path = path.Join(u.Path, DefaultV2MembersPrefix)
-
 	c := &httpClient{
 		transport: tr,
 		endpoint:  *u,
@@ -65,7 +63,8 @@ type httpMembersAPI struct {
 }
 
 func (m *httpMembersAPI) List() ([]httptypes.Member, error) {
-	code, body, err := m.client.doWithTimeout(&membersAPIActionList{})
+	req := &membersAPIActionList{}
+	code, body, err := m.client.doWithTimeout(req)
 	if err != nil {
 		return nil, err
 	}
@@ -119,6 +118,7 @@ func (m *httpMembersAPI) Remove(memberID string) error {
 type membersAPIActionList struct{}
 
 func (l *membersAPIActionList) httpRequest(ep url.URL) *http.Request {
+	ep.Path = path.Join(ep.Path, DefaultV2MembersPrefix)
 	req, _ := http.NewRequest("GET", ep.String(), nil)
 	return req
 }
@@ -128,7 +128,7 @@ type membersAPIActionRemove struct {
 }
 
 func (d *membersAPIActionRemove) httpRequest(ep url.URL) *http.Request {
-	ep.Path = path.Join(ep.Path, d.memberID)
+	ep.Path = path.Join(ep.Path, DefaultV2MembersPrefix, d.memberID)
 	req, _ := http.NewRequest("DELETE", ep.String(), nil)
 	return req
 }
@@ -138,6 +138,7 @@ type membersAPIActionAdd struct {
 }
 
 func (a *membersAPIActionAdd) httpRequest(ep url.URL) *http.Request {
+	ep.Path = path.Join(ep.Path, DefaultV2MembersPrefix)
 	m := httptypes.MemberCreateRequest{PeerURLs: a.peerURLs}
 	b, _ := json.Marshal(&m)
 	req, _ := http.NewRequest("POST", ep.String(), bytes.NewReader(b))

+ 4 - 4
client/members_test.go

@@ -25,7 +25,7 @@ import (
 )
 
 func TestMembersAPIActionList(t *testing.T) {
-	ep := url.URL{Scheme: "http", Host: "example.com/v2/members"}
+	ep := url.URL{Scheme: "http", Host: "example.com"}
 	act := &membersAPIActionList{}
 
 	wantURL := &url.URL{
@@ -42,7 +42,7 @@ func TestMembersAPIActionList(t *testing.T) {
 }
 
 func TestMembersAPIActionAdd(t *testing.T) {
-	ep := url.URL{Scheme: "http", Host: "example.com/v2/admin/members"}
+	ep := url.URL{Scheme: "http", Host: "example.com"}
 	act := &membersAPIActionAdd{
 		peerURLs: types.URLs([]url.URL{
 			url.URL{Scheme: "https", Host: "127.0.0.1:8081"},
@@ -53,7 +53,7 @@ func TestMembersAPIActionAdd(t *testing.T) {
 	wantURL := &url.URL{
 		Scheme: "http",
 		Host:   "example.com",
-		Path:   "/v2/admin/members",
+		Path:   "/v2/members",
 	}
 	wantHeader := http.Header{
 		"Content-Type": []string{"application/json"},
@@ -68,7 +68,7 @@ func TestMembersAPIActionAdd(t *testing.T) {
 }
 
 func TestMembersAPIActionRemove(t *testing.T) {
-	ep := url.URL{Scheme: "http", Host: "example.com/v2/members"}
+	ep := url.URL{Scheme: "http", Host: "example.com"}
 	act := &membersAPIActionRemove{memberID: "XXX"}
 
 	wantURL := &url.URL{