mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
refactor move http status logic to server.go
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -20,6 +21,8 @@ import (
|
||||
type PeerServer struct {
|
||||
raftServer raft.Server
|
||||
server *Server
|
||||
httpServer *http.Server
|
||||
listener net.Listener
|
||||
joinIndex uint64
|
||||
name string
|
||||
url string
|
||||
@@ -88,7 +91,7 @@ func NewPeerServer(name string, path string, url string, listenHost string, tlsC
|
||||
}
|
||||
|
||||
// Start the raft server
|
||||
func (s *PeerServer) ListenAndServe(snapshot bool, cluster []string) {
|
||||
func (s *PeerServer) ListenAndServe(snapshot bool, cluster []string) error {
|
||||
// LoadSnapshot
|
||||
if snapshot {
|
||||
err := s.raftServer.LoadSnapshot()
|
||||
@@ -137,8 +140,60 @@ func (s *PeerServer) ListenAndServe(snapshot bool, cluster []string) {
|
||||
}
|
||||
|
||||
// start to response to raft requests
|
||||
go s.startTransport(s.tlsConf.Scheme, s.tlsConf.Server)
|
||||
return s.startTransport(s.tlsConf.Scheme, s.tlsConf.Server)
|
||||
}
|
||||
|
||||
// Overridden version of net/http added so we can manage the listener.
|
||||
func (s *PeerServer) listenAndServe() error {
|
||||
addr := s.httpServer.Addr
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
l, e := net.Listen("tcp", addr)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
s.listener = l
|
||||
return s.httpServer.Serve(l)
|
||||
}
|
||||
|
||||
// Overridden version of net/http added so we can manage the listener.
|
||||
func (s *PeerServer) listenAndServeTLS(certFile, keyFile string) error {
|
||||
addr := s.httpServer.Addr
|
||||
if addr == "" {
|
||||
addr = ":https"
|
||||
}
|
||||
config := &tls.Config{}
|
||||
if s.httpServer.TLSConfig != nil {
|
||||
*config = *s.httpServer.TLSConfig
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsListener := tls.NewListener(conn, config)
|
||||
s.listener = tlsListener
|
||||
return s.httpServer.Serve(tlsListener)
|
||||
}
|
||||
|
||||
// Stops the server.
|
||||
func (s *PeerServer) Close() {
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves the underlying Raft server.
|
||||
@@ -177,12 +232,12 @@ func (s *PeerServer) startAsFollower(cluster []string) {
|
||||
}
|
||||
|
||||
// Start to listen and response raft command
|
||||
func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
|
||||
func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) error {
|
||||
log.Infof("raft server [name %s, listen on %s, advertised url %s]", s.name, s.listenHost, s.url)
|
||||
|
||||
raftMux := http.NewServeMux()
|
||||
|
||||
server := &http.Server{
|
||||
s.httpServer = &http.Server{
|
||||
Handler: raftMux,
|
||||
TLSConfig: &tlsConf,
|
||||
Addr: s.listenHost,
|
||||
@@ -201,9 +256,9 @@ func (s *PeerServer) startTransport(scheme string, tlsConf tls.Config) {
|
||||
raftMux.HandleFunc("/etcdURL", s.EtcdURLHttpHandler)
|
||||
|
||||
if scheme == "http" {
|
||||
log.Fatal(server.ListenAndServe())
|
||||
return s.listenAndServe()
|
||||
} else {
|
||||
log.Fatal(server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
|
||||
return s.listenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -22,6 +24,7 @@ type Server struct {
|
||||
http.Server
|
||||
peerServer *PeerServer
|
||||
registry *Registry
|
||||
listener net.Listener
|
||||
store store.Store
|
||||
name string
|
||||
url string
|
||||
@@ -157,13 +160,66 @@ func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Reque
|
||||
}
|
||||
|
||||
// Start to listen and response etcd client command
|
||||
func (s *Server) ListenAndServe() {
|
||||
func (s *Server) ListenAndServe() error {
|
||||
log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url)
|
||||
|
||||
if s.tlsConf.Scheme == "http" {
|
||||
log.Fatal(s.Server.ListenAndServe())
|
||||
return s.listenAndServe()
|
||||
} else {
|
||||
log.Fatal(s.Server.ListenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile))
|
||||
return s.listenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden version of net/http added so we can manage the listener.
|
||||
func (s *Server) listenAndServe() error {
|
||||
addr := s.Server.Addr
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
l, e := net.Listen("tcp", addr)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
s.listener = l
|
||||
return s.Server.Serve(l)
|
||||
}
|
||||
|
||||
// Overridden version of net/http added so we can manage the listener.
|
||||
func (s *Server) listenAndServeTLS(certFile, keyFile string) error {
|
||||
addr := s.Server.Addr
|
||||
if addr == "" {
|
||||
addr = ":https"
|
||||
}
|
||||
config := &tls.Config{}
|
||||
if s.Server.TLSConfig != nil {
|
||||
*config = *s.Server.TLSConfig
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsListener := tls.NewListener(conn, config)
|
||||
s.listener = tlsListener
|
||||
return s.Server.Serve(tlsListener)
|
||||
}
|
||||
|
||||
// Stops the server.
|
||||
func (s *Server) Close() {
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +249,12 @@ func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Reque
|
||||
} else {
|
||||
e, _ := result.(*store.Event)
|
||||
b, _ = json.Marshal(e)
|
||||
w.WriteHeader(e.HttpStatusCode())
|
||||
|
||||
if e.IsCreated() {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
w.Write(b)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
var c raft.Command
|
||||
|
||||
vars := mux.Vars(req)
|
||||
key := "/" + vars["key"]
|
||||
|
||||
@@ -23,11 +25,14 @@ func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Update", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
|
||||
prevValue, valueOk := req.Form["prevValue"]
|
||||
prevIndexStr, indexOk := req.Form["prevIndex"]
|
||||
prevExist, existOk := req.Form["prevExist"]
|
||||
_, valueOk := req.Form["prevValue"]
|
||||
prevValue := req.Form.Get("prevValue")
|
||||
|
||||
var c raft.Command
|
||||
_, indexOk := req.Form["prevIndex"]
|
||||
prevIndexStr := req.Form.Get("prevIndex")
|
||||
|
||||
_, existOk := req.Form["prevExist"]
|
||||
prevExist := req.Form.Get("prevExist")
|
||||
|
||||
// Set handler: create a new node or replace the old one.
|
||||
if !valueOk && !indexOk && !existOk {
|
||||
@@ -36,13 +41,13 @@ func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
|
||||
// update with test
|
||||
if existOk {
|
||||
if prevExist[0] == "false" {
|
||||
if prevExist == "false" {
|
||||
// Create command: create a new node. Fail, if a node already exists
|
||||
// Ignore prevIndex and prevValue
|
||||
return CreateHandler(w, req, s, key, value, expireTime)
|
||||
}
|
||||
|
||||
if prevExist[0] == "true" && !indexOk && !valueOk {
|
||||
if prevExist == "true" && !indexOk && !valueOk {
|
||||
return UpdateHandler(w, req, s, key, value, expireTime)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +55,7 @@ func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
var prevIndex uint64
|
||||
|
||||
if indexOk {
|
||||
prevIndex, err = strconv.ParseUint(prevIndexStr[0], 10, 64)
|
||||
prevIndex, err = strconv.ParseUint(prevIndexStr, 10, 64)
|
||||
|
||||
// bad previous index
|
||||
if err != nil {
|
||||
@@ -61,7 +66,7 @@ func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
}
|
||||
|
||||
if valueOk {
|
||||
if prevValue[0] == "" {
|
||||
if prevValue == "" {
|
||||
return etcdErr.NewError(etcdErr.EcodePrevValueRequired, "CompareAndSwap", store.UndefIndex, store.UndefTerm)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +74,7 @@ func PutHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
c = &store.CompareAndSwapCommand{
|
||||
Key: key,
|
||||
Value: value,
|
||||
PrevValue: prevValue[0],
|
||||
PrevValue: prevValue,
|
||||
PrevIndex: prevIndex,
|
||||
}
|
||||
|
||||
|
||||
152
server/v2/tests/get_handler_test.go
Normal file
152
server/v2/tests/get_handler_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/server"
|
||||
"github.com/coreos/etcd/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ensures that a value can be retrieve for a given key.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl localhost:4001/v2/keys/foo/bar
|
||||
//
|
||||
func TestV2GetKey(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.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"))
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "get", "")
|
||||
assert.Equal(t, body["key"], "/foo/bar", "")
|
||||
assert.Equal(t, body["value"], "XXX", "")
|
||||
assert.Equal(t, body["index"], 3, "")
|
||||
assert.Equal(t, body["term"], 0, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a directory of values can be recursively retrieved for a given key.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/x -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/y/z -d value=YYY
|
||||
// $ curl localhost:4001/v2/keys/foo -d recursive=true
|
||||
//
|
||||
func TestV2GetKeyRecursively(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/x"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
v.Set("value", "YYY")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/y/z"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
resp, _ = tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo?recursive=true"))
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "get", "")
|
||||
assert.Equal(t, body["key"], "/foo", "")
|
||||
assert.Equal(t, body["dir"], true, "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
assert.Equal(t, len(body["kvs"].([]interface{})), 2, "")
|
||||
|
||||
kv0 := body["kvs"].([]interface{})[0].(map[string]interface{})
|
||||
assert.Equal(t, kv0["key"], "/foo/x", "")
|
||||
assert.Equal(t, kv0["value"], "XXX", "")
|
||||
|
||||
kv1 := body["kvs"].([]interface{})[1].(map[string]interface{})
|
||||
assert.Equal(t, kv1["key"], "/foo/y", "")
|
||||
assert.Equal(t, kv1["dir"], true, "")
|
||||
|
||||
kvs2 := kv1["kvs"].([]interface{})[0].(map[string]interface{})
|
||||
assert.Equal(t, kvs2["key"], "/foo/y/z", "")
|
||||
assert.Equal(t, kvs2["value"], "YYY", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a watcher can wait for a value to be set and return it to the client.
|
||||
//
|
||||
// $ curl localhost:4001/v2/keys/foo/bar?wait=true
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
//
|
||||
func TestV2WatchKey(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
var body map[string]interface{}
|
||||
go func() {
|
||||
resp, _ := tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?wait=true"))
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
}()
|
||||
|
||||
// Make sure response didn't fire early.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
assert.Nil(t, body, "")
|
||||
|
||||
// Set a value.
|
||||
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)
|
||||
|
||||
// A response should follow from the GET above.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
assert.NotNil(t, body, "")
|
||||
assert.Equal(t, body["action"], "set", "")
|
||||
assert.Equal(t, body["key"], "/foo/bar", "")
|
||||
assert.Equal(t, body["value"], "XXX", "")
|
||||
assert.Equal(t, body["index"], 3, "")
|
||||
assert.Equal(t, body["term"], 0, "")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Ensures that a watcher can wait for a value to be set after a given index.
|
||||
//
|
||||
// $ curl localhost:4001/v2/keys/foo/bar?wait=true&waitIndex=4
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY
|
||||
//
|
||||
func TestV2WatchKeyWithIndex(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
var body map[string]interface{}
|
||||
go func() {
|
||||
resp, _ := tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?wait=true&waitIndex=5"))
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
}()
|
||||
|
||||
// Make sure response didn't fire early.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
assert.Nil(t, body, "")
|
||||
|
||||
// Set a value (before given index).
|
||||
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)
|
||||
|
||||
// Make sure response didn't fire early.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
assert.Nil(t, body, "")
|
||||
|
||||
// Set a value (before given index).
|
||||
v.Set("value", "YYY")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
// A response should follow from the GET above.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
assert.NotNil(t, body, "")
|
||||
assert.Equal(t, body["action"], "set", "")
|
||||
assert.Equal(t, body["key"], "/foo/bar", "")
|
||||
assert.Equal(t, body["value"], "YYY", "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
assert.Equal(t, body["term"], 0, "")
|
||||
})
|
||||
}
|
||||
|
||||
38
server/v2/tests/post_handler_test.go
Normal file
38
server/v2/tests/post_handler_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/server"
|
||||
"github.com/coreos/etcd/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ensures a unique value is added to the key's children.
|
||||
//
|
||||
// $ curl -X POST localhost:4001/v2/keys/foo/bar
|
||||
// $ curl -X POST localhost:4001/v2/keys/foo/bar
|
||||
// $ curl -X POST localhost:4001/v2/keys/foo/baz
|
||||
//
|
||||
func TestV2CreateUnique(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
// POST should add index to list.
|
||||
resp, _ := tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "create", "")
|
||||
assert.Equal(t, body["key"], "/foo/bar/3", "")
|
||||
assert.Equal(t, body["dir"], true, "")
|
||||
assert.Equal(t, body["index"], 3, "")
|
||||
|
||||
// Second POST should add next index to list.
|
||||
resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["key"], "/foo/bar/4", "")
|
||||
|
||||
// POST to a different key should add index to that list.
|
||||
resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/baz"), nil)
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["key"], "/foo/baz/5", "")
|
||||
})
|
||||
}
|
||||
280
server/v2/tests/put_handler_test.go
Normal file
280
server/v2/tests/put_handler_test.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/server"
|
||||
"github.com/coreos/etcd/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ensures that a key is set to a given value.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
//
|
||||
func TestV2SetKey(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
resp, err := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBody(resp)
|
||||
assert.Nil(t, err, "")
|
||||
assert.Equal(t, string(body), `{"action":"set","key":"/foo/bar","value":"XXX","index":3,"term":0}`, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a time-to-live is added to a key.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=20
|
||||
//
|
||||
func TestV2SetKeyWithTTL(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
t0 := time.Now()
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("ttl", "20")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["ttl"], 20, "")
|
||||
|
||||
// Make sure the expiration date is correct.
|
||||
expiration, _ := time.Parse(time.RFC3339Nano, body["expiration"].(string))
|
||||
assert.Equal(t, expiration.Sub(t0) / time.Second, 20, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that an invalid time-to-live is returned as an error.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=bad_ttl
|
||||
//
|
||||
func TestV2SetKeyWithBadTTL(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("ttl", "bad_ttl")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 202, "")
|
||||
assert.Equal(t, body["message"], "The given TTL in POST form is not a number", "")
|
||||
assert.Equal(t, body["cause"], "Update", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is conditionally set only if it previously did not exist.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false
|
||||
//
|
||||
func TestV2CreateKeySuccess(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("prevExist", "false")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["value"], "XXX", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is not conditionally because it previously existed.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false
|
||||
//
|
||||
func TestV2CreateKeyFail(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("prevExist", "false")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 105, "")
|
||||
assert.Equal(t, body["message"], "Already exists", "")
|
||||
assert.Equal(t, body["cause"], "/foo/bar", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is conditionally set only if it previously did exist.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true
|
||||
//
|
||||
func TestV2UpdateKeySuccess(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)
|
||||
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevExist", "true")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "update", "")
|
||||
assert.Equal(t, body["prevValue"], "XXX", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is not conditionally set if it previously did not exist.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=true
|
||||
//
|
||||
func TestV2UpdateKeyFailOnValue(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo"), v)
|
||||
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevExist", "true")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 100, "")
|
||||
assert.Equal(t, body["message"], "Key Not Found", "")
|
||||
assert.Equal(t, body["cause"], "/foo/bar", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is not conditionally set if it previously did not exist.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX -d prevExist=true
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=true
|
||||
//
|
||||
func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevExist", "true")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 100, "")
|
||||
assert.Equal(t, body["message"], "Key Not Found", "")
|
||||
assert.Equal(t, body["cause"], "/foo", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is set only if the previous index matches.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=3
|
||||
//
|
||||
func TestV2SetKeyCASOnIndexSuccess(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)
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevIndex", "3")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "compareAndSwap", "")
|
||||
assert.Equal(t, body["prevValue"], "XXX", "")
|
||||
assert.Equal(t, body["value"], "YYY", "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is not set if the previous index does not match.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=10
|
||||
//
|
||||
func TestV2SetKeyCASOnIndexFail(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)
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevIndex", "10")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 101, "")
|
||||
assert.Equal(t, body["message"], "Test Failed", "")
|
||||
assert.Equal(t, body["cause"], "[ != XXX] [10 != 3]", "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that an error is thrown if an invalid previous index is provided.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=bad_index
|
||||
//
|
||||
func TestV2SetKeyCASWithInvalidIndex(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevIndex", "bad_index")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 203, "")
|
||||
assert.Equal(t, body["message"], "The given index in POST form is not a number", "")
|
||||
assert.Equal(t, body["cause"], "CompareAndSwap", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is set only if the previous value matches.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX
|
||||
//
|
||||
func TestV2SetKeyCASOnValueSuccess(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)
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevValue", "XXX")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "compareAndSwap", "")
|
||||
assert.Equal(t, body["prevValue"], "XXX", "")
|
||||
assert.Equal(t, body["value"], "YYY", "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a key is not set if the previous value does not match.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA
|
||||
//
|
||||
func TestV2SetKeyCASOnValueFail(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)
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevValue", "AAA")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 101, "")
|
||||
assert.Equal(t, body["message"], "Test Failed", "")
|
||||
assert.Equal(t, body["cause"], "[AAA != XXX] [0 != 3]", "")
|
||||
assert.Equal(t, body["index"], 4, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that an error is returned if a blank prevValue is set.
|
||||
//
|
||||
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevValue=
|
||||
//
|
||||
func TestV2SetKeyCASWithMissingValueFails(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("prevValue", "")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["errorCode"], 201, "")
|
||||
assert.Equal(t, body["message"], "PrevValue is Required in POST form", "")
|
||||
assert.Equal(t, body["cause"], "CompareAndSwap", "")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user