client: explicitly carry API prefix around

release-2.0
Brian Waldon 2014-10-30 15:42:34 -07:00
parent dee912f2fd
commit 7c1f4a9baf
4 changed files with 47 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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