From 7563a13621ad61a33bfe7e158e47b1565ee8c180 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 21 Aug 2013 14:21:17 +0300 Subject: [PATCH 1/9] Fix error code in README.md There is no error code with number 404. It returns 100 when no key exist. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 525b52d3a..e7392495c 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Now you can try to get the key by sending: curl -L http://127.0.0.1:4001/v1/keys/foo ``` -If the TTL has expired, the key will be deleted, and you will be returned a 404. +If the TTL has expired, the key will be deleted, and you will be returned a 100. ```json {"errorCode":100,"message":"Key Not Found","cause":"/foo"} From 800c4718c1691ddec877c5828c69dc3c112b9088 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 21 Aug 2013 14:43:43 +0300 Subject: [PATCH 2/9] Fix api urls in examples They don't work without the v1 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 525b52d3a..259b0462a 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ Let the join two more nodes to this cluster using the -C argument: Get the machines in the cluster: ```sh -curl -L http://127.0.0.1:4001/machines +curl -L http://127.0.0.1:4001/v1/machines ``` We should see there are three nodes in the cluster @@ -380,7 +380,7 @@ The key of the machine is based on the ```commit index``` when it was added. The Also try to get the current leader in the cluster ``` -curl -L http://127.0.0.1:4001/leader +curl -L http://127.0.0.1:4001/v1/leader ``` The first server we set up should be the leader, if it has not dead during these commands. @@ -409,7 +409,7 @@ curl -L http://127.0.0.1:4002/v1/keys/foo A new leader should have been elected. ``` -curl -L http://127.0.0.1:4001/leader +curl -L http://127.0.0.1:4001/v1/leader ``` ``` From e856acf05e828fcfb31687232153ac294a55811f Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Aug 2013 17:25:38 -0400 Subject: [PATCH 3/9] fix(build): If you quote in shell you're going to have a bad time. --- build | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/build b/build index dafab2f52..a4fe58737 100755 --- a/build +++ b/build @@ -1,25 +1,25 @@ #!/bin/bash ETCD_PACKAGE=github.com/coreos/etcd -export GOPATH=${PWD} -SRC_DIR=$GOPATH/src -ETCD_DIR=$SRC_DIR/$ETCD_PACKAGE +export GOPATH="${PWD}" +SRC_DIR="$GOPATH/src" +ETCD_DIR="$SRC_DIR/$ETCD_PACKAGE" -ETCD_BASE=$(dirname ${ETCD_DIR}) -if [ ! -d ${ETCD_BASE} ]; then - mkdir -p ${ETCD_BASE} +ETCD_BASE=$(dirname "${ETCD_DIR}") +if [ ! -d "${ETCD_BASE}" ]; then + mkdir -p "${ETCD_BASE}" fi -if [ ! -h ${ETCD_DIR} ]; then - ln -s ../../../ ${ETCD_DIR} +if [ ! -h "${ETCD_DIR}" ]; then + ln -s ../../../ "${ETCD_DIR}" fi for i in third_party/*; do - if [ $i = "third_party/src" ]; then + if [ "$i" = "third_party/src" ]; then continue fi - cp -R $i src/ + cp -R "$i" src/ done ./scripts/release-version > release_version.go -go build ${ETCD_PACKAGE} +go build "${ETCD_PACKAGE}" From 4f436ae70ae21429baea18ec5bb62c88a2ac9b0e Mon Sep 17 00:00:00 2001 From: Andrew Hobden Date: Wed, 21 Aug 2013 14:43:25 -0700 Subject: [PATCH 4/9] Added a PUT handler that copies the behavior of the POST handler. Fixes #139. --- etcd_handlers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etcd_handlers.go b/etcd_handlers.go index 4c4673983..60e7b35b5 100644 --- a/etcd_handlers.go +++ b/etcd_handlers.go @@ -48,6 +48,8 @@ func Multiplexer(w http.ResponseWriter, req *http.Request) error { return GetHttpHandler(w, req) case "POST": return SetHttpHandler(w, req) + case "PUT": + return SetHttpHandler(w, req) case "DELETE": return DeleteHttpHandler(w, req) default: From 50d53f3ae08865a94ce274dcf32f8fb68598e92f Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Thu, 22 Aug 2013 13:13:41 -0700 Subject: [PATCH 5/9] fix(README): add cd etcd on build instructions thanks to edw in #coreos for the fix --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 04ccae37f..f6a4aa86b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ You can build etcd from source: ```sh git clone https://github.com/coreos/etcd +cd etcd ./build ``` From 91aed9e232f89031978c078989cc50ed87b62283 Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Wed, 21 Aug 2013 14:21:17 +0300 Subject: [PATCH 6/9] Fix error code in README.md There is no error code with number 404. It returns 100 when no key exist. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 259b0462a..04ccae37f 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Now you can try to get the key by sending: curl -L http://127.0.0.1:4001/v1/keys/foo ``` -If the TTL has expired, the key will be deleted, and you will be returned a 404. +If the TTL has expired, the key will be deleted, and you will be returned a 100. ```json {"errorCode":100,"message":"Key Not Found","cause":"/foo"} From 6345e02d20189d90240e2e41c51718c493b043c4 Mon Sep 17 00:00:00 2001 From: "Fabrizio (Misto) Milo" Date: Thu, 22 Aug 2013 15:04:17 -0700 Subject: [PATCH 7/9] test and set creates the key if key does not exists. fixes #96 --- store/store.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/store/store.go b/store/store.go index d37345f4d..c1da0fc27 100644 --- a/store/store.go +++ b/store/store.go @@ -465,12 +465,16 @@ func (s *Store) TestAndSet(key string, prevValue string, value string, expireTim resp := s.internalGet(key) if resp == nil { - return nil, etcdErr.NewError(100, "testandset: "+key) + if prevValue != "" { + errmsg := fmt.Sprintf("TestAndSet: key not found and previousValue is not empty %s:%s ", key, prevValue) + return nil, etcdErr.NewError(100, errmsg) + } + return s.internalSet(key, value, expireTime, index) } if resp.Value == prevValue { - // If test success, do set + // If test succeed, do set return s.internalSet(key, value, expireTime, index) } else { From a543d644b401b3aaa36d7219700de595853bb874 Mon Sep 17 00:00:00 2001 From: "Fabrizio (Misto) Milo" Date: Wed, 21 Aug 2013 16:39:23 -0700 Subject: [PATCH 8/9] Split raw get into rawGetNode and rawGetNodeList --- store/store.go | 194 +++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/store/store.go b/store/store.go index c1da0fc27..e4eb79db0 100644 --- a/store/store.go +++ b/store/store.go @@ -325,6 +325,62 @@ func (s *Store) Get(key string) ([]byte, error) { return json.Marshal(resps) } +func (s *Store) rawGetNode(key string, node *Node) ([]*Response, error) { + resps := make([]*Response, 1) + + isExpire := !node.ExpireTime.Equal(PERMANENT) + + resps[0] = &Response{ + Action: "GET", + Index: s.Index, + Key: key, + Value: node.Value, + } + + // Update ttl + if isExpire { + TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second) + resps[0].Expiration = &node.ExpireTime + resps[0].TTL = TTL + } + + return resps, nil +} + +func (s *Store) rawGetNodeList(key string, keys []string, nodes []*Node) ([]*Response, error) { + resps := make([]*Response, len(nodes)) + + // TODO: check if nodes and keys are the same length + for i := 0; i < len(nodes); i++ { + var TTL int64 + var isExpire bool = false + + isExpire = !nodes[i].ExpireTime.Equal(PERMANENT) + + resps[i] = &Response{ + Action: "GET", + Index: s.Index, + Key: path.Join(key, keys[i]), + } + + if len(nodes[i].Value) != 0 { + resps[i].Value = nodes[i].Value + } else { + resps[i].Dir = true + } + + // Update ttl + if isExpire { + TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second) + resps[i].Expiration = &nodes[i].ExpireTime + resps[i].TTL = TTL + } + + } + + return resps, nil +} + func (s *Store) RawGet(key string) ([]*Response, error) { // Update stats s.BasicStats.Gets++ @@ -332,68 +388,18 @@ func (s *Store) RawGet(key string) ([]*Response, error) { key = path.Clean("/" + key) nodes, keys, ok := s.Tree.list(key) - - if ok { - - node, ok := nodes.(*Node) - - if ok { - resps := make([]*Response, 1) - - isExpire := !node.ExpireTime.Equal(PERMANENT) - - resps[0] = &Response{ - Action: "GET", - Index: s.Index, - Key: key, - Value: node.Value, - } - - // Update ttl - if isExpire { - TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second) - resps[0].Expiration = &node.ExpireTime - resps[0].TTL = TTL - } - - return resps, nil - } - - nodes, _ := nodes.([]*Node) - - resps := make([]*Response, len(nodes)) - for i := 0; i < len(nodes); i++ { - - var TTL int64 - var isExpire bool = false - - isExpire = !nodes[i].ExpireTime.Equal(PERMANENT) - - resps[i] = &Response{ - Action: "GET", - Index: s.Index, - Key: path.Join(key, keys[i]), - } - - if len(nodes[i].Value) != 0 { - resps[i].Value = nodes[i].Value - } else { - resps[i].Dir = true - } - - // Update ttl - if isExpire { - TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second) - resps[i].Expiration = &nodes[i].ExpireTime - resps[i].TTL = TTL - } - - } - - return resps, nil + if !ok { + return nil, etcdErr.NewError(100, "get: "+key) } - return nil, etcdErr.NewError(100, "get: "+key) + switch node := nodes.(type) { + case *Node: + return s.rawGetNode(key, node) + case []*Node: + return s.rawGetNodeList(key, keys, node) + default: + panic("invalid cast ") + } } func (s *Store) Delete(key string, index uint64) ([]byte, error) { @@ -415,43 +421,41 @@ func (s *Store) internalDelete(key string, index uint64) ([]byte, error) { node, ok := s.Tree.get(key) - if ok { - - resp := Response{ - Action: "DELETE", - Key: key, - PrevValue: node.Value, - Index: index, - } - - if node.ExpireTime.Equal(PERMANENT) { - - s.Tree.delete(key) - - } else { - resp.Expiration = &node.ExpireTime - // Kill the expire go routine - node.update <- PERMANENT - s.Tree.delete(key) - - } - - msg, err := json.Marshal(resp) - - s.watcher.notify(resp) - - // notify the messager - if s.messager != nil && err == nil { - s.messager <- string(msg) - } - - s.addToResponseMap(index, &resp) - - return msg, err - - } else { + if !ok { return nil, etcdErr.NewError(100, "delete: "+key) } + + resp := Response{ + Action: "DELETE", + Key: key, + PrevValue: node.Value, + Index: index, + } + + if node.ExpireTime.Equal(PERMANENT) { + + s.Tree.delete(key) + + } else { + resp.Expiration = &node.ExpireTime + // Kill the expire go routine + node.update <- PERMANENT + s.Tree.delete(key) + + } + + msg, err := json.Marshal(resp) + + s.watcher.notify(resp) + + // notify the messager + if s.messager != nil && err == nil { + s.messager <- string(msg) + } + + s.addToResponseMap(index, &resp) + + return msg, err } // Set the value of the key to the value if the given prevValue is equal to the value of the key From 6108f8536f932dad27947b77ec4a9c6cf8aabbec Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 23 Aug 2013 01:37:20 -0400 Subject: [PATCH 9/9] add testandset test --- store/store_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/store/store_test.go b/store/store_test.go index 001f5ddd5..6bd719008 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -31,6 +31,42 @@ func TestStoreGetDelete(t *testing.T) { } } +func TestTestAndSet(t *testing.T) { + s := CreateStore(100) + s.Set("foo", "bar", time.Unix(0, 0), 1) + + _, err := s.TestAndSet("foo", "barbar", "barbar", time.Unix(0, 0), 2) + + if err == nil { + t.Fatalf("test bar == barbar should fail") + } + + _, err = s.TestAndSet("foo", "bar", "barbar", time.Unix(0, 0), 3) + + if err != nil { + t.Fatalf("test bar == bar should succeed") + } + + _, err = s.TestAndSet("foo", "", "barbar", time.Unix(0, 0), 4) + + if err == nil { + t.Fatalf("test empty == bar should fail") + } + + _, err = s.TestAndSet("fooo", "bar", "barbar", time.Unix(0, 0), 5) + + if err == nil { + t.Fatalf("test bar == non-existing key should fail") + } + + _, err = s.TestAndSet("fooo", "", "bar", time.Unix(0, 0), 6) + + if err != nil { + t.Fatalf("test empty == non-existing key should succeed") + } + +} + func TestSaveAndRecovery(t *testing.T) { s := CreateStore(100)