From 373199fe468b017071d527c39641bfcdf7313af3 Mon Sep 17 00:00:00 2001 From: rick Date: Sat, 30 Nov 2013 16:25:26 -0700 Subject: [PATCH] support CreateAndDelete through the v2 server --- server/v2/delete_handler.go | 36 ++++++++- server/v2/tests/delete_handler_test.go | 107 +++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/server/v2/delete_handler.go b/server/v2/delete_handler.go index 7afc02f04..c98c2768e 100644 --- a/server/v2/delete_handler.go +++ b/server/v2/delete_handler.go @@ -2,15 +2,47 @@ package v2 import ( "net/http" + "strconv" + etcdErr "github.com/coreos/etcd/error" "github.com/gorilla/mux" ) func DeleteHandler(w http.ResponseWriter, req *http.Request, s Server) error { vars := mux.Vars(req) key := "/" + vars["key"] - recursive := (req.FormValue("recursive") == "true") - c := s.Store().CommandFactory().CreateDeleteCommand(key, recursive) + req.ParseForm() + _, valueOk := req.Form["prevValue"] + _, indexOk := req.Form["prevIndex"] + + if !valueOk && !indexOk { + recursive := (req.Form.Get("recursive") == "true") + + c := s.Store().CommandFactory().CreateDeleteCommand(key, recursive) + return s.Dispatch(c, w, req) + } + + var err error + prevIndex := uint64(0) + prevValue := req.Form.Get("prevValue") + + if indexOk { + prevIndexStr := req.Form.Get("prevIndex") + prevIndex, err = strconv.ParseUint(prevIndexStr, 10, 64) + + // bad previous index + if err != nil { + return etcdErr.NewError(etcdErr.EcodeIndexNaN, "CompareAndDelete", s.Store().Index()) + } + } + + if valueOk { + if prevValue == "" { + return etcdErr.NewError(etcdErr.EcodePrevValueRequired, "CompareAndDelete", s.Store().Index()) + } + } + + c := s.Store().CommandFactory().CreateCompareAndDeleteCommand(key, prevValue, prevIndex) return s.Dispatch(c, w, req) } diff --git a/server/v2/tests/delete_handler_test.go b/server/v2/tests/delete_handler_test.go index 997127a9e..b602e0764 100644 --- a/server/v2/tests/delete_handler_test.go +++ b/server/v2/tests/delete_handler_test.go @@ -27,3 +27,110 @@ func TestV2DeleteKey(t *testing.T) { assert.Equal(t, string(body), `{"action":"delete","key":"/foo/bar","prevValue":"XXX","modifiedIndex":2}`, "") }) } + +// Ensures that a key is deleted only if the previous index matches. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=1 +// +func TestV2DeleteKeyCADOnIndexSuccess(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevIndex=1"), v) + body := tests.ReadBodyJSON(resp) + assert.Equal(t, body["action"], "compareAndDelete", "") + assert.Equal(t, body["prevValue"], "XXX", "") + assert.Equal(t, body["modifiedIndex"], 2, "") + }) +} + +// Ensures that a key is not deleted if the previous index does not matche. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=2 +// +func TestV2DeleteKeyCADOnIndexFail(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevIndex=100"), v) + body := tests.ReadBodyJSON(resp) + fmt.Println(body) + assert.Equal(t, body["errorCode"], 101) + }) +} + +// Ensures that an error is thrown if an invalid previous index is provided. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=bad_index +// +func TestV2DeleteKeyCADWithInvalidIndex(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevIndex=bad_index"), v) + body := tests.ReadBodyJSON(resp) + assert.Equal(t, body["errorCode"], 203) + }) +} + +// Ensures that a key is deleted only if the previous value matches. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=XXX +// +func TestV2DeleteKeyCADOnValueSuccess(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevValue=XXX"), v) + body := tests.ReadBodyJSON(resp) + assert.Equal(t, body["action"], "compareAndDelete", "") + assert.Equal(t, body["prevValue"], "XXX", "") + assert.Equal(t, body["modifiedIndex"], 2, "") + }) +} + +// Ensures that a key is not deleted if the previous value does not matche. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=YYY +// +func TestV2DeleteKeyCADOnValueFail(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevValue=YYY"), v) + body := tests.ReadBodyJSON(resp) + assert.Equal(t, body["errorCode"], 101) + }) +} + +// Ensures that an error is thrown if an invalid previous value is provided. +// +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX +// $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex= +// +func TestV2DeleteKeyCADWithInvalidValue(t *testing.T) { + tests.RunServer(func(s *server.Server) { + v := url.Values{} + v.Set("value", "XXX") + resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v) + tests.ReadBody(resp) + resp, _ = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?prevValue="), v) + body := tests.ReadBodyJSON(resp) + assert.Equal(t, body["errorCode"], 201) + }) +}