diff --git a/server/v2/tests/delete_handler_test.go b/server/v2/tests/delete_handler_test.go index d5e1c8206..a7dd6e687 100644 --- a/server/v2/tests/delete_handler_test.go +++ b/server/v2/tests/delete_handler_test.go @@ -26,7 +26,7 @@ func TestV2DeleteKey(t *testing.T) { assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") - assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":3,"createdIndex":2}}`, "") + assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "") }) } @@ -48,7 +48,7 @@ func TestV2DeleteEmptyDirectory(t *testing.T) { assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") - assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") + assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "") }) } @@ -70,7 +70,7 @@ func TestV2DeleteNonEmptyDirectory(t *testing.T) { assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") - assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") + assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "") }) } @@ -87,7 +87,7 @@ func TestV2DeleteDirectoryRecursiveImpliesDir(t *testing.T) { assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") - assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") + assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "") }) } diff --git a/store/event.go b/store/event.go index 00885af80..5674b132c 100644 --- a/store/event.go +++ b/store/event.go @@ -12,8 +12,9 @@ const ( ) type Event struct { - Action string `json:"action"` - Node *NodeExtern `json:"node,omitempty"` + Action string `json:"action"` + Node *NodeExtern `json:"node,omitempty"` + PrevNode *NodeExtern `json:"prevNode,omitempty"` } func newEvent(action string, key string, modifiedIndex, createdIndex uint64) *Event { diff --git a/store/node.go b/store/node.go index 71c0a64d7..e17fcf204 100644 --- a/store/node.go +++ b/store/node.go @@ -231,9 +231,9 @@ func (n *node) Remove(dir, recursive bool, callback func(path string)) *etcdErr. return nil } -func (n *node) Repr(recurisive, sorted bool) NodeExtern { +func (n *node) Repr(recurisive, sorted bool) *NodeExtern { if n.IsDir() { - node := NodeExtern{ + node := &NodeExtern{ Key: n.Path, Dir: true, ModifiedIndex: n.ModifiedIndex, @@ -272,7 +272,7 @@ func (n *node) Repr(recurisive, sorted bool) NodeExtern { return node } - node := NodeExtern{ + node := &NodeExtern{ Key: n.Path, Value: n.Value, ModifiedIndex: n.ModifiedIndex, diff --git a/store/node_extern.go b/store/node_extern.go index 514d5c8cf..727d16c93 100644 --- a/store/node_extern.go +++ b/store/node_extern.go @@ -20,7 +20,7 @@ type NodeExtern struct { CreatedIndex uint64 `json:"createdIndex,omitempty"` } -type NodeExterns []NodeExtern +type NodeExterns []*NodeExtern // interfaces for sorting func (ns NodeExterns) Len() int { diff --git a/store/store.go b/store/store.go index 08d585056..23168d18c 100644 --- a/store/store.go +++ b/store/store.go @@ -225,6 +225,7 @@ func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint s.CurrentIndex++ e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex) + e.PrevNode = n.Repr(false, false) eNode := e.Node eNode.PrevValue = n.Value @@ -267,6 +268,7 @@ func (s *store) Delete(nodePath string, dir, recursive bool) (*Event, error) { nextIndex := s.CurrentIndex + 1 e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex) + e.PrevNode = n.Repr(false, false) eNode := e.Node if n.IsDir() { @@ -326,6 +328,7 @@ func (s *store) CompareAndDelete(nodePath string, prevValue string, prevIndex ui s.CurrentIndex++ e := newEvent(CompareAndDelete, nodePath, s.CurrentIndex, n.CreatedIndex) + e.PrevNode = n.Repr(false, false) callback := func(path string) { // notify function // notify the watchers with deleted set true @@ -412,6 +415,7 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) ( } e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex) + e.PrevNode = n.Repr(false, false) eNode := e.Node if n.IsDir() && len(newValue) != 0 { @@ -482,6 +486,7 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique, if n.IsDir() { return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex) } + e.PrevNode = n.Repr(false, false) eNode.PrevValue, _ = n.Read() n.Remove(false, false, nil) @@ -557,6 +562,7 @@ func (s *store) DeleteExpiredKeys(cutoff time.Time) { s.CurrentIndex++ e := newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex) + e.PrevNode = node.Repr(false, false) callback := func(path string) { // notify function // notify the watchers with deleted set true diff --git a/store/store_test.go b/store/store_test.go index b29fea627..65f29fffc 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -112,6 +112,11 @@ func TestSet(t *testing.T) { assert.Nil(t, e.Node.Expiration, "") assert.Equal(t, e.Node.TTL, 0, "") assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "") + // check prevNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "", "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") // Set /dir as a directory e, err = s.Set("/dir", true, "", Permanent) @@ -199,6 +204,12 @@ func TestStoreUpdateValue(t *testing.T) { assert.Equal(t, e.Node.Value, "baz", "") assert.Equal(t, e.Node.TTL, 0, "") assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "") + // check prevNode + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") + assert.Equal(t, e.PrevNode.TTL, 0, "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") + e, _ = s.Get("/foo", false, false) assert.Equal(t, e.Node.Value, "baz", "") @@ -212,6 +223,12 @@ func TestStoreUpdateValue(t *testing.T) { assert.Equal(t, e.Node.Value, "", "") assert.Equal(t, e.Node.TTL, 0, "") assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "") + // check prevNode + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "baz", "") + assert.Equal(t, e.PrevNode.TTL, 0, "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "") + e, _ = s.Get("/foo", false, false) assert.Equal(t, e.Node.Value, "", "") } @@ -278,6 +295,10 @@ func TestStoreDeleteValue(t *testing.T) { e, err := s.Delete("/foo", false, false) assert.Nil(t, err, "") assert.Equal(t, e.Action, "delete", "") + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") } // Ensure that the store can delete a directory if recursive is specified. @@ -290,6 +311,10 @@ func TestStoreDeleteDiretory(t *testing.T) { e, err := s.Delete("/foo", true, false) assert.Nil(t, err, "") assert.Equal(t, e.Action, "delete", "") + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Dir, true, "") // create directory /foo and directory /foo/bar s.Create("/foo/bar", true, "", false, Permanent) @@ -346,6 +371,13 @@ func TestStoreCompareAndDeletePrevValue(t *testing.T) { assert.Nil(t, err, "") assert.Equal(t, e.Action, "compareAndDelete", "") assert.Equal(t, e.Node.Key, "/foo", "") + + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") + assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") } func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) { @@ -366,6 +398,12 @@ func TestStoreCompareAndDeletePrevIndex(t *testing.T) { e, err := s.CompareAndDelete("/foo", "", 1) assert.Nil(t, err, "") assert.Equal(t, e.Action, "compareAndDelete", "") + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") + assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") } func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) { @@ -400,6 +438,13 @@ func TestStoreCompareAndSwapPrevValue(t *testing.T) { assert.Equal(t, e.Action, "compareAndSwap", "") assert.Equal(t, e.Node.PrevValue, "bar", "") assert.Equal(t, e.Node.Value, "baz", "") + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") + assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") + e, _ = s.Get("/foo", false, false) assert.Equal(t, e.Node.Value, "baz", "") } @@ -426,6 +471,13 @@ func TestStoreCompareAndSwapPrevIndex(t *testing.T) { assert.Equal(t, e.Action, "compareAndSwap", "") assert.Equal(t, e.Node.PrevValue, "bar", "") assert.Equal(t, e.Node.Value, "baz", "") + // check pervNode + assert.NotNil(t, e.PrevNode, "") + assert.Equal(t, e.PrevNode.Key, "/foo", "") + assert.Equal(t, e.PrevNode.Value, "bar", "") + assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "") + assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "") + e, _ = s.Get("/foo", false, false) assert.Equal(t, e.Node.Value, "baz", "") }