diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f557d0a86..22fad64d4 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/kelseyhightower/confd", - "GoVersion": "go1.4.2", + "GoVersion": "go1.4.1", "Deps": [ { "ImportPath": "github.com/BurntSushi/toml", @@ -12,8 +12,8 @@ }, { "ImportPath": "github.com/coreos/go-etcd/etcd", - "Comment": "v0.2.0-rc1-120-g23142f6", - "Rev": "23142f6773a676cc2cae8dd0cb90b2ea761c853f" + "Comment": "v0.4.6", + "Rev": "6aa2da5a7a905609c93036b9307185a04a5a84a5" }, { "ImportPath": "github.com/kelseyhightower/memkv", diff --git a/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go index 5d8b45a2d..2741cd098 100644 --- a/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go +++ b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests.go @@ -3,6 +3,7 @@ package etcd import ( "errors" "fmt" + "io" "io/ioutil" "math/rand" "net/http" @@ -179,6 +180,7 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { // we connect to a leader sleep := 25 * time.Millisecond maxSleep := time.Second + for attempt := 0; ; attempt++ { if attempt > 0 { select { @@ -192,7 +194,7 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { } } - logger.Debug("Connecting to etcd: attempt", attempt+1, "for", rr.RelativePath) + logger.Debug("Connecting to etcd: attempt ", attempt+1, " for ", rr.RelativePath) if rr.Method == "GET" && c.config.Consistency == WEAK_CONSISTENCY { // If it's a GET and consistency level is set to WEAK, @@ -214,21 +216,29 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { logger.Debug("send.request.to ", httpPath, " | method ", rr.Method) - reqLock.Lock() - if rr.Values == nil { - if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil { - return nil, err - } - } else { - body := strings.NewReader(rr.Values.Encode()) - if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil { - return nil, err + req, err := func() (*http.Request, error) { + reqLock.Lock() + defer reqLock.Unlock() + + if rr.Values == nil { + if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil { + return nil, err + } + } else { + body := strings.NewReader(rr.Values.Encode()) + if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil { + return nil, err + } + + req.Header.Set("Content-Type", + "application/x-www-form-urlencoded; param=value") } + return req, nil + }() - req.Header.Set("Content-Type", - "application/x-www-form-urlencoded; param=value") + if err != nil { + return nil, err } - reqLock.Unlock() resp, err = c.httpClient.Do(req) defer func() { @@ -248,7 +258,7 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { // network error, change a machine! if err != nil { - logger.Debug("network error:", err.Error()) + logger.Debug("network error: ", err.Error()) lastResp := http.Response{} if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil { return nil, checkErr @@ -259,13 +269,13 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { } // if there is no error, it should receive response - logger.Debug("recv.response.from", httpPath) + logger.Debug("recv.response.from ", httpPath) if validHttpStatusCode[resp.StatusCode] { // try to read byte code and break the loop respBody, err = ioutil.ReadAll(resp.Body) if err == nil { - logger.Debug("recv.success.", httpPath) + logger.Debug("recv.success ", httpPath) break } // ReadAll error may be caused due to cancel request @@ -274,6 +284,15 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { return nil, ErrRequestCancelled default: } + + if err == io.ErrUnexpectedEOF { + // underlying connection was closed prematurely, probably by timeout + // TODO: empty body or unexpectedEOF can cause http.Transport to get hosed; + // this allows the client to detect that and take evasive action. Need + // to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed. + respBody = []byte{} + break + } } // if resp is TemporaryRedirect, set the new leader and retry @@ -286,7 +305,7 @@ func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) { // Update cluster leader based on redirect location // because it should point to the leader address c.cluster.updateLeaderFromURL(u) - logger.Debug("recv.response.relocate", u.String()) + logger.Debug("recv.response.relocate ", u.String()) } resp.Body.Close() continue @@ -360,11 +379,13 @@ func buildValues(value string, ttl uint64) url.Values { return v } -// convert key string to http path exclude version +// convert key string to http path exclude version, including URL escaping // for example: key[foo] -> path[keys/foo] +// key[/%z] -> path[keys/%25z] // key[/] -> path[keys/] func keyToPath(key string) string { - p := path.Join("keys", key) + // URL-escape our key, except for slashes + p := strings.Replace(url.QueryEscape(path.Join("keys", key)), "%2F", "/", -1) // corner case: if key is "/" or "//" ect // path join will clear the tailing "/" diff --git a/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests_test.go b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests_test.go new file mode 100644 index 000000000..7a2bd190a --- /dev/null +++ b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/requests_test.go @@ -0,0 +1,22 @@ +package etcd + +import "testing" + +func TestKeyToPath(t *testing.T) { + tests := []struct { + key string + wpath string + }{ + {"", "keys/"}, + {"foo", "keys/foo"}, + {"foo/bar", "keys/foo/bar"}, + {"%z", "keys/%25z"}, + {"/", "keys/"}, + } + for i, tt := range tests { + path := keyToPath(tt.key) + if path != tt.wpath { + t.Errorf("#%d: path = %s, want %s", i, path, tt.wpath) + } + } +} diff --git a/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go index cb0d56747..e2840cf35 100644 --- a/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go +++ b/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd/set_update_create.go @@ -13,7 +13,7 @@ func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) { return raw.Unmarshal() } -// Set sets the given key to a directory. +// SetDir sets the given key to a directory. // It will create a new directory or replace the old key value pair by a directory. // It will not replace a existing directory. func (c *Client) SetDir(key string, ttl uint64) (*Response, error) {