mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge remote-tracking branch 'upstream/master' into feature-parametric-timeout
Conflicts: Dockerfile server/usage.go tests/server_utils.go
This commit is contained in:
commit
a06f5e74af
@ -7,4 +7,5 @@ RUN apt-get install -y golang
|
||||
ADD . /opt/etcd
|
||||
RUN cd /opt/etcd && ./build
|
||||
EXPOSE 4001 7001
|
||||
ENTRYPOINT ["/opt/etcd/etcd", "-addr", "0.0.0.0:4001", "-bind-addr", "0.0.0.0"]
|
||||
ENTRYPOINT ["/opt/etcd/etcd"]
|
||||
|
||||
|
270
README.md
270
README.md
@ -83,7 +83,15 @@ curl -L http://127.0.0.1:4001/v2/keys/message -X PUT -d value="Hello world"
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/message","value":"Hello world","modifiedIndex":2}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 2,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 2,
|
||||
"value": "Hello world"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This response contains four fields.
|
||||
@ -112,7 +120,15 @@ curl -L http://127.0.0.1:4001/v2/keys/message
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"get","key":"/message","value":"Hello world","modifiedIndex":2}
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"createdIndex": 2,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 2,
|
||||
"value": "Hello world"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -125,10 +141,19 @@ curl -L http://127.0.0.1:4001/v2/keys/message -XPUT -d value="Hello etcd"
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/message","prevValue":"Hello world","value":"Hello etcd","index":3}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 3,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 3,
|
||||
"prevValue": "Hello world",
|
||||
"value": "Hello etcd"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that the `prevValue` is set to the previous value of the key - `Hello world`.
|
||||
Notice that `node.prevValue` is set to the previous value of the key - `Hello world`.
|
||||
It is useful when you want to atomically set a value to a key and get its old value.
|
||||
|
||||
|
||||
@ -141,7 +166,15 @@ curl -L http://127.0.0.1:4001/v2/keys/message -XDELETE
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"delete","key":"/message","prevValue":"Hello etcd","modifiedIndex":4}
|
||||
{
|
||||
"action": "delete",
|
||||
"node": {
|
||||
"createdIndex": 3,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 4,
|
||||
"prevValue": "Hello etcd"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -155,7 +188,17 @@ curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -d ttl=5
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo","value":"bar","expiration":"2013-11-12T20:21:22.629352334-05:00","ttl":5,"modifiedIndex":5}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 5,
|
||||
"expiration": "2013-12-04T12:01:21.874888581-08:00",
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 5,
|
||||
"ttl": 5,
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note the two new fields in response:
|
||||
@ -175,7 +218,12 @@ curl -L http://127.0.0.1:4001/v2/keys/foo
|
||||
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","index":6}
|
||||
{
|
||||
"cause": "/foo",
|
||||
"errorCode": 100,
|
||||
"index": 6,
|
||||
"message": "Key Not Found"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -201,7 +249,15 @@ curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar
|
||||
The first terminal should get the notification and return with the same response as the set request.
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo","value":"bar","modifiedIndex":7}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 7,
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 7,
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
However, the watch command can do more than this.
|
||||
@ -248,7 +304,12 @@ curl -L http://127.0.0.1:4001/v2/keys/foo?prevExist=false -XPUT -d value=three
|
||||
The error code explains the problem:
|
||||
|
||||
```json
|
||||
{"errorCode":105,"message":"Already exists","cause":"/foo","index":39776}
|
||||
{
|
||||
"cause": "/foo",
|
||||
"errorCode": 105,
|
||||
"index": 39776,
|
||||
"message": "Already exists"
|
||||
}
|
||||
```
|
||||
|
||||
Now lets provide a `prevValue` parameter:
|
||||
@ -260,7 +321,12 @@ curl -L http://127.0.0.1:4001/v2/keys/foo?prevValue=two -XPUT -d value=three
|
||||
This will try to compare the previous value of the key and the previous value we provided. If they are equal, the value of the key will change to three.
|
||||
|
||||
```json
|
||||
{"errorCode":101,"message":"Test Failed","cause":"[two != one] [0 != 8]","index":8}
|
||||
{
|
||||
"cause": "[two != one] [0 != 8]",
|
||||
"errorCode": 101,
|
||||
"index": 8,
|
||||
"message": "Test Failed"
|
||||
}
|
||||
```
|
||||
|
||||
which means `CompareAndSwap` failed.
|
||||
@ -274,10 +340,19 @@ curl -L http://127.0.0.1:4001/v2/keys/foo?prevValue=one -XPUT -d value=two
|
||||
The response should be
|
||||
|
||||
```json
|
||||
{"action":"compareAndSwap","key":"/foo","prevValue":"one","value":"two","modifiedIndex":9}
|
||||
{
|
||||
"action": "compareAndSwap",
|
||||
"node": {
|
||||
"createdIndex": 8,
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 9,
|
||||
"prevValue": "one",
|
||||
"value": "two"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We successfully changed the value from “one” to “two” since we gave the correct previous value.
|
||||
We successfully changed the value from "one" to "two" since we gave the correct previous value.
|
||||
|
||||
|
||||
### Listing a directory
|
||||
@ -295,7 +370,15 @@ curl -L http://127.0.0.1:4001/v2/keys/foo_dir/foo -XPUT -d value=bar
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo_dir/foo","value":"bar","modifiedIndex":10}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 2,
|
||||
"key": "/foo_dir/foo",
|
||||
"modifiedIndex": 2,
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we can list the keys under root `/`:
|
||||
@ -307,7 +390,21 @@ curl -L http://127.0.0.1:4001/v2/keys/
|
||||
We should see the response as an array of items:
|
||||
|
||||
```json
|
||||
{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/foo_dir","dir":true,"modifiedIndex":10}],"modifiedIndex":0}
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"dir": true,
|
||||
"key": "/",
|
||||
"nodes": [
|
||||
{
|
||||
"createdIndex": 2,
|
||||
"dir": true,
|
||||
"key": "/foo_dir",
|
||||
"modifiedIndex": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here we can see `/foo` is a key-value pair under `/` and `/foo_dir` is a directory.
|
||||
@ -318,7 +415,29 @@ curl -L http://127.0.0.1:4001/v2/keys/?recursive=true
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/foo_dir","dir":true,"kvs":[{"key":"/foo_dir/foo","value":"bar","modifiedIndex":10}],"modifiedIndex":10}],"modifiedIndex":0}
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"dir": true,
|
||||
"key": "/",
|
||||
"nodes": [
|
||||
{
|
||||
"createdIndex": 2,
|
||||
"dir": true,
|
||||
"key": "/foo_dir",
|
||||
"modifiedIndex": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"createdIndex": 2,
|
||||
"key": "/foo_dir/foo",
|
||||
"modifiedIndex": 2,
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -333,7 +452,15 @@ curl -L http://127.0.0.1:4001/v2/keys/foo_dir?recursive=true -XDELETE
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"delete","key":"/foo_dir","dir":true,"modifiedIndex":11}
|
||||
{
|
||||
"action": "delete",
|
||||
"node": {
|
||||
"createdIndex": 10,
|
||||
"dir": true,
|
||||
"key": "/foo_dir",
|
||||
"modifiedIndex": 11
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -349,7 +476,15 @@ curl -L http://127.0.0.1:4001/v2/keys/_message -XPUT -d value="Hello hidden worl
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/_message","value":"Hello hidden world","modifiedIndex":12}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 3,
|
||||
"key": "/_message",
|
||||
"modifiedIndex": 3,
|
||||
"value": "Hello hidden world"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -360,7 +495,15 @@ curl -L http://127.0.0.1:4001/v2/keys/message -XPUT -d value="Hello world"
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/message","value":"Hello world","modifiedIndex":13}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 4,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 4,
|
||||
"value": "Hello world"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now let's try to get a listing of keys under the root directory, `/`:
|
||||
@ -370,7 +513,27 @@ curl -L http://127.0.0.1:4001/v2/keys/
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/message","value":"Hello world","modifiedIndex":13}],"modifiedIndex":0}
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"dir": true,
|
||||
"key": "/",
|
||||
"nodes": [
|
||||
{
|
||||
"createdIndex": 2,
|
||||
"dir": true,
|
||||
"key": "/foo_dir",
|
||||
"modifiedIndex": 2
|
||||
},
|
||||
{
|
||||
"createdIndex": 4,
|
||||
"key": "/message",
|
||||
"modifiedIndex": 4,
|
||||
"value": "Hello world"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here we see the `/message` key but our hidden `/_message` key is not returned.
|
||||
@ -421,7 +584,13 @@ SSLv3, TLS handshake, Finished (20):
|
||||
And also the response from the etcd server:
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo","prevValue":"bar","value":"bar","modifiedIndex":3}
|
||||
{
|
||||
"action": "set",
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 3,
|
||||
"prevValue": "bar",
|
||||
"value": "bar"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -468,7 +637,16 @@ TLS handshake, Finished (20)
|
||||
And also the response from the server:
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo","prevValue":"bar","value":"bar","modifiedIndex":3}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 12,
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 12,
|
||||
"prevValue": "two",
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -516,7 +694,35 @@ curl -L http://127.0.0.1:4001/v2/keys/_etcd/machines
|
||||
```
|
||||
|
||||
```json
|
||||
[{"action":"get","key":"/_etcd/machines/machine1","value":"raft=http://127.0.0.1:7001\u0026etcd=http://127.0.0.1:4001","index":1},{"action":"get","key":"/_etcd/machines/machine2","value":"raft=http://127.0.0.1:7002\u0026etcd=http://127.0.0.1:4002","index":1},{"action":"get","key":"/_etcd/machines/machine3","value":"raft=http://127.0.0.1:7003\u0026etcd=http://127.0.0.1:4003","index":1}]
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"createdIndex": 1,
|
||||
"dir": true,
|
||||
"key": "/_etcd/machines",
|
||||
"modifiedIndex": 1,
|
||||
"nodes": [
|
||||
{
|
||||
"createdIndex": 1,
|
||||
"key": "/_etcd/machines/machine1",
|
||||
"modifiedIndex": 1,
|
||||
"value": "raft=http://127.0.0.1:7001&etcd=http://127.0.0.1:4001"
|
||||
},
|
||||
{
|
||||
"createdIndex": 2,
|
||||
"key": "/_etcd/machines/machine2",
|
||||
"modifiedIndex": 2,
|
||||
"value": "raft=http://127.0.0.1:7002&etcd=http://127.0.0.1:4002"
|
||||
},
|
||||
{
|
||||
"createdIndex": 3,
|
||||
"key": "/_etcd/machines/machine3",
|
||||
"modifiedIndex": 3,
|
||||
"value": "raft=http://127.0.0.1:7003&etcd=http://127.0.0.1:4003"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can also get the current leader in the cluster:
|
||||
@ -538,7 +744,15 @@ curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"set","key":"/foo","value":"bar","modifiedIndex":4}
|
||||
{
|
||||
"action": "set",
|
||||
"node": {
|
||||
"createdIndex": 4,
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 4,
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -579,7 +793,15 @@ curl -L http://127.0.0.1:4002/v2/keys/foo
|
||||
```
|
||||
|
||||
```json
|
||||
{"action":"get","key":"/foo","value":"bar","index":4}
|
||||
{
|
||||
"action": "get",
|
||||
"node": {
|
||||
"createdIndex": 4,
|
||||
"key": "/foo",
|
||||
"modifiedIndex": 4,
|
||||
"value": "bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
10
etcd.go
10
etcd.go
@ -52,16 +52,6 @@ func main() {
|
||||
profile(config.CPUProfileFile)
|
||||
}
|
||||
|
||||
// Only guess the machine name if there is no data dir specified
|
||||
// because the info file will should have our name
|
||||
if config.Name == "" && config.DataDir == "" {
|
||||
config.NameFromHostname()
|
||||
}
|
||||
|
||||
if config.DataDir == "" && config.Name != "" {
|
||||
config.DataDirFromName()
|
||||
}
|
||||
|
||||
if config.DataDir == "" {
|
||||
log.Fatal("The data dir was not set and could not be guessed from machine name")
|
||||
}
|
||||
|
128
mod/lock/v2/acquire_handler.go
Normal file
128
mod/lock/v2/acquire_handler.go
Normal file
@ -0,0 +1,128 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-etcd/etcd"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// acquireHandler attempts to acquire a lock on the given key.
|
||||
// The "key" parameter specifies the resource to lock.
|
||||
// The "ttl" parameter specifies how long the lock will persist for.
|
||||
// The "timeout" parameter specifies how long the request should wait for the lock.
|
||||
func (h *handler) acquireHandler(w http.ResponseWriter, req *http.Request) {
|
||||
h.client.SyncCluster()
|
||||
|
||||
// Setup connection watcher.
|
||||
closeNotifier, _ := w.(http.CloseNotifier)
|
||||
closeChan := closeNotifier.CloseNotify()
|
||||
|
||||
// Parse "key" and "ttl" query parameters.
|
||||
vars := mux.Vars(req)
|
||||
keypath := path.Join(prefix, vars["key"])
|
||||
ttl, err := strconv.Atoi(req.FormValue("ttl"))
|
||||
if err != nil {
|
||||
http.Error(w, "invalid ttl: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse "timeout" parameter.
|
||||
var timeout int
|
||||
if len(req.FormValue("timeout")) == 0 {
|
||||
timeout = -1
|
||||
} else if timeout, err = strconv.Atoi(req.FormValue("timeout")); err != nil {
|
||||
http.Error(w, "invalid timeout: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
timeout = timeout + 1
|
||||
|
||||
// Create an incrementing id for the lock.
|
||||
resp, err := h.client.AddChild(keypath, "-", uint64(ttl))
|
||||
if err != nil {
|
||||
http.Error(w, "add lock index error: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
indexpath := resp.Node.Key
|
||||
|
||||
// Keep updating TTL to make sure lock request is not expired before acquisition.
|
||||
stop := make(chan bool)
|
||||
go h.ttlKeepAlive(indexpath, ttl, stop)
|
||||
|
||||
// Monitor for broken connection.
|
||||
stopWatchChan := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-closeChan:
|
||||
stopWatchChan <- true
|
||||
case <-stop:
|
||||
// Stop watching for connection disconnect.
|
||||
}
|
||||
}()
|
||||
|
||||
// Extract the lock index.
|
||||
index, _ := strconv.Atoi(path.Base(resp.Node.Key))
|
||||
|
||||
// Wait until we successfully get a lock or we get a failure.
|
||||
var success bool
|
||||
for {
|
||||
// Read all indices.
|
||||
resp, err = h.client.Get(keypath, true, true)
|
||||
if err != nil {
|
||||
http.Error(w, "lock children lookup error: " + err.Error(), http.StatusInternalServerError)
|
||||
break
|
||||
}
|
||||
indices := extractResponseIndices(resp)
|
||||
waitIndex := resp.Node.ModifiedIndex
|
||||
prevIndex := findPrevIndex(indices, index)
|
||||
|
||||
// If there is no previous index then we have the lock.
|
||||
if prevIndex == 0 {
|
||||
success = true
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise watch previous index until it's gone.
|
||||
_, err = h.client.Watch(path.Join(keypath, strconv.Itoa(prevIndex)), waitIndex, false, nil, stopWatchChan)
|
||||
if err == etcd.ErrWatchStoppedByUser {
|
||||
break
|
||||
} else if err != nil {
|
||||
http.Error(w, "lock watch error: " + err.Error(), http.StatusInternalServerError)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check for connection disconnect before we write the lock index.
|
||||
select {
|
||||
case <-stopWatchChan:
|
||||
success = false
|
||||
default:
|
||||
}
|
||||
|
||||
// Stop the ttl keep-alive.
|
||||
close(stop)
|
||||
|
||||
if success {
|
||||
// Write lock index to response body if we acquire the lock.
|
||||
h.client.Update(indexpath, "-", uint64(ttl))
|
||||
w.Write([]byte(strconv.Itoa(index)))
|
||||
} else {
|
||||
// Make sure key is deleted if we couldn't acquire.
|
||||
h.client.Delete(indexpath, false)
|
||||
}
|
||||
}
|
||||
|
||||
// ttlKeepAlive continues to update a key's TTL until the stop channel is closed.
|
||||
func (h *handler) ttlKeepAlive(k string, ttl int, stop chan bool) {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Duration(ttl / 2) * time.Second):
|
||||
h.client.Update(k, "-", uint64(ttl))
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
30
mod/lock/v2/get_index_handler.go
Normal file
30
mod/lock/v2/get_index_handler.go
Normal file
@ -0,0 +1,30 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// getIndexHandler retrieves the current lock index.
|
||||
func (h *handler) getIndexHandler(w http.ResponseWriter, req *http.Request) {
|
||||
h.client.SyncCluster()
|
||||
|
||||
vars := mux.Vars(req)
|
||||
keypath := path.Join(prefix, vars["key"])
|
||||
|
||||
// Read all indices.
|
||||
resp, err := h.client.Get(keypath, true, true)
|
||||
if err != nil {
|
||||
http.Error(w, "lock children lookup error: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Write out the index of the last one to the response body.
|
||||
indices := extractResponseIndices(resp)
|
||||
if len(indices) > 0 {
|
||||
w.Write([]byte(strconv.Itoa(indices[0])))
|
||||
}
|
||||
}
|
58
mod/lock/v2/handler.go
Normal file
58
mod/lock/v2/handler.go
Normal file
@ -0,0 +1,58 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"sort"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/coreos/go-etcd/etcd"
|
||||
)
|
||||
|
||||
const prefix = "/_etcd/mod/lock"
|
||||
|
||||
// handler manages the lock HTTP request.
|
||||
type handler struct {
|
||||
*mux.Router
|
||||
client *etcd.Client
|
||||
}
|
||||
|
||||
// NewHandler creates an HTTP handler that can be registered on a router.
|
||||
func NewHandler(addr string) (http.Handler) {
|
||||
h := &handler{
|
||||
Router: mux.NewRouter(),
|
||||
client: etcd.NewClient([]string{addr}),
|
||||
}
|
||||
h.StrictSlash(false)
|
||||
h.HandleFunc("/{key:.*}", h.getIndexHandler).Methods("GET")
|
||||
h.HandleFunc("/{key:.*}", h.acquireHandler).Methods("POST")
|
||||
h.HandleFunc("/{key_with_index:.*}", h.renewLockHandler).Methods("PUT")
|
||||
h.HandleFunc("/{key_with_index:.*}", h.releaseLockHandler).Methods("DELETE")
|
||||
return h
|
||||
}
|
||||
|
||||
|
||||
// extractResponseIndices extracts a sorted list of indicies from a response.
|
||||
func extractResponseIndices(resp *etcd.Response) []int {
|
||||
var indices []int
|
||||
for _, node := range resp.Node.Nodes {
|
||||
if index, _ := strconv.Atoi(path.Base(node.Key)); index > 0 {
|
||||
indices = append(indices, index)
|
||||
}
|
||||
}
|
||||
sort.Ints(indices)
|
||||
return indices
|
||||
}
|
||||
|
||||
// findPrevIndex retrieves the previous index before the given index.
|
||||
func findPrevIndex(indices []int, idx int) int {
|
||||
var prevIndex int
|
||||
for _, index := range indices {
|
||||
if index == idx {
|
||||
break
|
||||
}
|
||||
prevIndex = index
|
||||
}
|
||||
return prevIndex
|
||||
}
|
24
mod/lock/v2/release_handler.go
Normal file
24
mod/lock/v2/release_handler.go
Normal file
@ -0,0 +1,24 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"path"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// releaseLockHandler deletes the lock.
|
||||
func (h *handler) releaseLockHandler(w http.ResponseWriter, req *http.Request) {
|
||||
h.client.SyncCluster()
|
||||
|
||||
vars := mux.Vars(req)
|
||||
keypath := path.Join(prefix, vars["key_with_index"])
|
||||
|
||||
// Delete the lock.
|
||||
_, err := h.client.Delete(keypath, false)
|
||||
if err != nil {
|
||||
http.Error(w, "delete lock index error: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
30
mod/lock/v2/renew_handler.go
Normal file
30
mod/lock/v2/renew_handler.go
Normal file
@ -0,0 +1,30 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"path"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// renewLockHandler attempts to update the TTL on an existing lock.
|
||||
// Returns a 200 OK if successful. Returns non-200 on error.
|
||||
func (h *handler) renewLockHandler(w http.ResponseWriter, req *http.Request) {
|
||||
h.client.SyncCluster()
|
||||
|
||||
vars := mux.Vars(req)
|
||||
keypath := path.Join(prefix, vars["key_with_index"])
|
||||
ttl, err := strconv.Atoi(req.FormValue("ttl"))
|
||||
if err != nil {
|
||||
http.Error(w, "invalid ttl: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Renew the lock, if it exists.
|
||||
_, err = h.client.Update(keypath, "-", uint64(ttl))
|
||||
if err != nil {
|
||||
http.Error(w, "renew lock index error: " + err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
188
mod/lock/v2/tests/handler_test.go
Normal file
188
mod/lock/v2/tests/handler_test.go
Normal file
@ -0,0 +1,188 @@
|
||||
package lock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/server"
|
||||
"github.com/coreos/etcd/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ensure that a lock can be acquired and released.
|
||||
func TestModLockAcquireAndRelease(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
// Acquire lock.
|
||||
body, err := testAcquireLock(s, "foo", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
// Check that we have the lock.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
// Release lock.
|
||||
body, err = testReleaseLock(s, "foo", 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "")
|
||||
|
||||
// Check that we have the lock.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure that a lock can be acquired and another process is blocked until released.
|
||||
func TestModLockBlockUntilAcquire(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
c := make(chan bool)
|
||||
|
||||
// Acquire lock #1.
|
||||
go func() {
|
||||
body, err := testAcquireLock(s, "foo", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
c <- true
|
||||
}()
|
||||
<- c
|
||||
|
||||
// Acquire lock #2.
|
||||
go func() {
|
||||
c <- true
|
||||
body, err := testAcquireLock(s, "foo", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "4")
|
||||
}()
|
||||
<- c
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Check that we have the lock #1.
|
||||
body, err := testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
// Release lock #1.
|
||||
body, err = testReleaseLock(s, "foo", 2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check that we have lock #2.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "4")
|
||||
|
||||
// Release lock #2.
|
||||
body, err = testReleaseLock(s, "foo", 4)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check that we have no lock.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure that a lock will be released after the TTL.
|
||||
func TestModLockExpireAndRelease(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
c := make(chan bool)
|
||||
|
||||
// Acquire lock #1.
|
||||
go func() {
|
||||
body, err := testAcquireLock(s, "foo", 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
c <- true
|
||||
}()
|
||||
<- c
|
||||
|
||||
// Acquire lock #2.
|
||||
go func() {
|
||||
c <- true
|
||||
body, err := testAcquireLock(s, "foo", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "4")
|
||||
}()
|
||||
<- c
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Check that we have the lock #1.
|
||||
body, err := testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
// Wait for lock #1 TTL.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Check that we have lock #2.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "4")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure that a lock can be renewed.
|
||||
func TestModLockRenew(t *testing.T) {
|
||||
tests.RunServer(func(s *server.Server) {
|
||||
// Acquire lock.
|
||||
body, err := testAcquireLock(s, "foo", 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Check that we have the lock.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
// Renew lock.
|
||||
body, err = testRenewLock(s, "foo", 2, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Check that we still have the lock.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "2")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Check that lock was released.
|
||||
body, err = testGetLockIndex(s, "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, "")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
func testAcquireLock(s *server.Server, key string, ttl int) (string, error) {
|
||||
resp, err := tests.PostForm(fmt.Sprintf("%s/mod/v2/lock/%s?ttl=%d", s.URL(), key, ttl), nil)
|
||||
ret := tests.ReadBody(resp)
|
||||
return string(ret), err
|
||||
}
|
||||
|
||||
func testGetLockIndex(s *server.Server, key string) (string, error) {
|
||||
resp, err := tests.Get(fmt.Sprintf("%s/mod/v2/lock/%s", s.URL(), key))
|
||||
ret := tests.ReadBody(resp)
|
||||
return string(ret), err
|
||||
}
|
||||
|
||||
func testReleaseLock(s *server.Server, key string, index int) (string, error) {
|
||||
resp, err := tests.DeleteForm(fmt.Sprintf("%s/mod/v2/lock/%s/%d", s.URL(), key, index), nil)
|
||||
ret := tests.ReadBody(resp)
|
||||
return string(ret), err
|
||||
}
|
||||
|
||||
func testRenewLock(s *server.Server, key string, index int, ttl int) (string, error) {
|
||||
resp, err := tests.PutForm(fmt.Sprintf("%s/mod/v2/lock/%s/%d?ttl=%d", s.URL(), key, index, ttl), nil)
|
||||
ret := tests.ReadBody(resp)
|
||||
return string(ret), err
|
||||
}
|
14
mod/mod.go
14
mod/mod.go
@ -6,6 +6,7 @@ import (
|
||||
"path"
|
||||
|
||||
"github.com/coreos/etcd/mod/dashboard"
|
||||
lock2 "github.com/coreos/etcd/mod/lock/v2"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
@ -16,11 +17,12 @@ func addSlash(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
func HttpHandler() (handler http.Handler) {
|
||||
modMux := mux.NewRouter()
|
||||
modMux.HandleFunc("/dashboard", addSlash)
|
||||
modMux.PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
|
||||
func HttpHandler(addr string) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/dashboard", addSlash)
|
||||
r.PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
|
||||
|
||||
return modMux
|
||||
// TODO: Use correct addr.
|
||||
r.PathPrefix("/v2/lock").Handler(http.StripPrefix("/v2/lock", lock2.NewHandler(addr)))
|
||||
return r
|
||||
}
|
||||
|
@ -134,6 +134,11 @@ func (c *Config) Load(arguments []string) error {
|
||||
return fmt.Errorf("sanitize: %v", err)
|
||||
}
|
||||
|
||||
// Force remove server configuration if specified.
|
||||
if c.Force {
|
||||
c.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -284,11 +289,6 @@ func (c *Config) LoadFlags(arguments []string) error {
|
||||
c.CorsOrigins = trimsplit(cors, ",")
|
||||
}
|
||||
|
||||
// Force remove server configuration if specified.
|
||||
if c.Force {
|
||||
c.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -410,6 +410,16 @@ func (c *Config) Sanitize() error {
|
||||
return fmt.Errorf("Peer Listen Host: %s", err)
|
||||
}
|
||||
|
||||
// Only guess the machine name if there is no data dir specified
|
||||
// because the info file should have our name
|
||||
if c.Name == "" && c.DataDir == "" {
|
||||
c.NameFromHostname()
|
||||
}
|
||||
|
||||
if c.DataDir == "" && c.Name != "" {
|
||||
c.DataDirFromName()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -441,7 +451,7 @@ func (c *Config) PeerTLSConfig() (TLSConfig, error) {
|
||||
return c.PeerTLSInfo().Config()
|
||||
}
|
||||
|
||||
// sanitizeURL will cleanup a host string in the format hostname:port and
|
||||
// sanitizeURL will cleanup a host string in the format hostname[:port] and
|
||||
// attach a schema.
|
||||
func sanitizeURL(host string, defaultScheme string) (string, error) {
|
||||
// Blank URLs are fine input, just return it
|
||||
@ -472,15 +482,23 @@ func sanitizeBindAddr(bindAddr string, addr string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ahost, aport, err := net.SplitHostPort(aurl.Host)
|
||||
// If it is a valid host:port simply return with no further checks.
|
||||
bhost, bport, err := net.SplitHostPort(bindAddr)
|
||||
if err == nil && bhost != "" {
|
||||
return bindAddr, nil
|
||||
}
|
||||
|
||||
// SplitHostPort makes the host optional, but we don't want that.
|
||||
if bhost == "" && bport != "" {
|
||||
return "", fmt.Errorf("IP required can't use a port only")
|
||||
}
|
||||
|
||||
// bindAddr doesn't have a port if we reach here so take the port from the
|
||||
// advertised URL.
|
||||
_, aport, err := net.SplitHostPort(aurl.Host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If the listen host isn't set use the advertised host
|
||||
if bindAddr == "" {
|
||||
bindAddr = ahost
|
||||
}
|
||||
|
||||
return net.JoinHostPort(bindAddr, aport), nil
|
||||
}
|
||||
|
@ -223,6 +223,29 @@ func TestConfigBindAddrFlag(t *testing.T) {
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host port overrides the advertised port
|
||||
func TestConfigBindAddrOverride(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", "127.0.0.1:4010"}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4010", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host inherits its port from the advertised addr
|
||||
func TestConfigBindAddrInheritPort(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", "127.0.0.1"}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4009", "")
|
||||
}
|
||||
|
||||
// Ensures that a port only argument errors out
|
||||
func TestConfigBindAddrErrorOnNoHost(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", ":4010"}), "")
|
||||
assert.Error(t, c.Sanitize())
|
||||
}
|
||||
|
||||
// Ensures that the peers can be parsed from the environment.
|
||||
func TestConfigPeersEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEERS", "coreos.com:4001,coreos.com:4002", func(c *Config) {
|
||||
@ -313,6 +336,24 @@ func TestConfigNameFlag(t *testing.T) {
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
}
|
||||
|
||||
// Ensures that a Name gets guessed if not specified
|
||||
func TestConfigNameGuess(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
name, _ := os.Hostname()
|
||||
assert.Equal(t, c.Name, name, "")
|
||||
}
|
||||
|
||||
// Ensures that a DataDir gets guessed if not specified
|
||||
func TestConfigDataDirGuess(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
name, _ := os.Hostname()
|
||||
assert.Equal(t, c.DataDir, name+".etcd", "")
|
||||
}
|
||||
|
||||
// Ensures that Snapshot can be parsed from the environment.
|
||||
func TestConfigSnapshotEnv(t *testing.T) {
|
||||
withEnv("ETCD_SNAPSHOT", "1", func(c *Config) {
|
||||
|
@ -135,7 +135,7 @@ func (s *Server) installV2() {
|
||||
|
||||
func (s *Server) installMod() {
|
||||
r := s.router
|
||||
r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler()))
|
||||
r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.url)))
|
||||
}
|
||||
|
||||
// Adds a v1 server handler to the router.
|
||||
|
@ -31,15 +31,15 @@ Cluster Configuration Options:
|
||||
should match the peer's '-peer-addr' flag.
|
||||
|
||||
Client Communication Options:
|
||||
-addr=<host:port> The public host:port used for client communication.
|
||||
-bind-addr=<host> The listening hostname used for client communication.
|
||||
-ca-file=<path> Path to the client CA file.
|
||||
-cert-file=<path> Path to the client cert file.
|
||||
-key-file=<path> Path to the client key file.
|
||||
-addr=<host:port> The public host:port used for client communication.
|
||||
-bind-addr=<host[:port]> The listening host:port used for client communication.
|
||||
-ca-file=<path> Path to the client CA file.
|
||||
-cert-file=<path> Path to the client cert file.
|
||||
-key-file=<path> Path to the client key file.
|
||||
|
||||
Peer Communication Options:
|
||||
-peer-addr=<host:port> The public host:port used for peer communication.
|
||||
-peer-bind-addr=<host> The listening hostname used for peer communication.
|
||||
-peer-bind-addr=<host[:port]> The listening host:port used for peer communication.
|
||||
-peer-ca-file=<path> Path to the peer CA file.
|
||||
-peer-cert-file=<path> Path to the peer cert file.
|
||||
-peer-key-file=<path> Path to the peer key file.
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
@ -24,9 +25,17 @@ func GetHandler(w http.ResponseWriter, req *http.Request, s Server) error {
|
||||
if req.FormValue("consistent") == "true" && s.State() != raft.Leader {
|
||||
leader := s.Leader()
|
||||
hostname, _ := s.ClientURL(leader)
|
||||
url := hostname + req.URL.Path
|
||||
log.Debugf("Redirect consistent get to %s", url)
|
||||
http.Redirect(w, req, url, http.StatusTemporaryRedirect)
|
||||
|
||||
url, err := url.Parse(hostname)
|
||||
if err != nil {
|
||||
log.Warn("Redirect cannot parse hostName ", hostname)
|
||||
return err
|
||||
}
|
||||
url.RawQuery = req.URL.RawQuery
|
||||
url.Path = req.URL.Path
|
||||
|
||||
log.Debugf("Redirect consistent get to %s", url.String())
|
||||
http.Redirect(w, req, url.String(), http.StatusTemporaryRedirect)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,11 @@ func TestV2DeleteKey(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)
|
||||
resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
resp, err = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{})
|
||||
resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{})
|
||||
body := tests.ReadBody(resp)
|
||||
assert.Nil(t, err, "")
|
||||
assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","prevValue":"XXX","modifiedIndex":2,"createdIndex":1}}`, "")
|
||||
assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","prevValue":"XXX","modifiedIndex":3,"createdIndex":2}}`, "")
|
||||
})
|
||||
}
|
||||
|
@ -20,16 +20,15 @@ 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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"))
|
||||
resp, _ = tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"))
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "get", "")
|
||||
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/bar", "")
|
||||
assert.Equal(t, node["value"], "XXX", "")
|
||||
assert.Equal(t, node["modifiedIndex"], 1, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -44,21 +43,20 @@ func TestV2GetKeyRecursively(t *testing.T) {
|
||||
v := url.Values{}
|
||||
v.Set("value", "XXX")
|
||||
v.Set("ttl", "10")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/x"), v)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%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"))
|
||||
resp, _ = tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?recursive=true"))
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "get", "")
|
||||
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo", "")
|
||||
assert.Equal(t, node["dir"], true, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 1, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
assert.Equal(t, len(node["nodes"].([]interface{})), 2, "")
|
||||
|
||||
node0 := node["nodes"].([]interface{})[0].(map[string]interface{})
|
||||
@ -86,7 +84,7 @@ func TestV2WatchKey(t *testing.T) {
|
||||
var body map[string]interface{}
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
resp, _ := tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?wait=true"))
|
||||
resp, _ := tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?wait=true"))
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
c <- true
|
||||
}()
|
||||
@ -98,7 +96,7 @@ func TestV2WatchKey(t *testing.T) {
|
||||
// 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
// A response should follow from the GET above.
|
||||
@ -117,7 +115,7 @@ func TestV2WatchKey(t *testing.T) {
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/bar", "")
|
||||
assert.Equal(t, node["value"], "XXX", "")
|
||||
assert.Equal(t, node["modifiedIndex"], 1, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -132,7 +130,7 @@ func TestV2WatchKeyWithIndex(t *testing.T) {
|
||||
var body map[string]interface{}
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
resp, _ := tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar?wait=true&waitIndex=2"))
|
||||
resp, _ := tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?wait=true&waitIndex=3"))
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
c <- true
|
||||
}()
|
||||
@ -144,7 +142,7 @@ func TestV2WatchKeyWithIndex(t *testing.T) {
|
||||
// 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
// Make sure response didn't fire early.
|
||||
@ -153,7 +151,7 @@ func TestV2WatchKeyWithIndex(t *testing.T) {
|
||||
|
||||
// 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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
|
||||
// A response should follow from the GET above.
|
||||
@ -172,6 +170,6 @@ func TestV2WatchKeyWithIndex(t *testing.T) {
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/bar", "")
|
||||
assert.Equal(t, node["value"], "YYY", "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 3, "")
|
||||
})
|
||||
}
|
||||
|
@ -18,25 +18,27 @@ import (
|
||||
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)
|
||||
resp, _ := tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "create", "")
|
||||
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/bar/1", "")
|
||||
assert.Equal(t, node["key"], "/foo/bar/2", "")
|
||||
assert.Equal(t, node["dir"], true, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 1, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
|
||||
// Second POST should add next index to list.
|
||||
resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
|
||||
resp, _ = tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
|
||||
node = body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/bar/2", "")
|
||||
assert.Equal(t, node["key"], "/foo/bar/3", "")
|
||||
|
||||
// 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)
|
||||
resp, _ = tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/baz"), nil)
|
||||
body = tests.ReadBodyJSON(resp)
|
||||
|
||||
node = body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["key"], "/foo/baz/3", "")
|
||||
assert.Equal(t, node["key"], "/foo/baz/4", "")
|
||||
})
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ 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)
|
||||
resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBody(resp)
|
||||
assert.Nil(t, err, "")
|
||||
assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"XXX","modifiedIndex":1,"createdIndex":1}}`, "")
|
||||
assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ func TestV2SetKeyWithTTL(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["ttl"], 20, "")
|
||||
@ -56,7 +56,7 @@ func TestV2SetKeyWithBadTTL(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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", "")
|
||||
@ -73,7 +73,7 @@ func TestV2CreateKeySuccess(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["value"], "XXX", "")
|
||||
@ -90,9 +90,9 @@ func TestV2CreateKeyFail(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%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", "")
|
||||
@ -110,12 +110,12 @@ func TestV2UpdateKeySuccess(t *testing.T) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Set("value", "XXX")
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "update", "")
|
||||
|
||||
@ -131,11 +131,11 @@ func TestV2UpdateKeySuccess(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%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", "")
|
||||
@ -153,7 +153,7 @@ func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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", "")
|
||||
@ -170,18 +170,17 @@ 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
tests.ReadBody(resp)
|
||||
v.Set("value", "YYY")
|
||||
v.Set("prevIndex", "1")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
v.Set("prevIndex", "2")
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "compareAndSwap", "")
|
||||
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["prevValue"], "XXX", "")
|
||||
assert.Equal(t, node["value"], "YYY", "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 3, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -194,16 +193,16 @@ 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%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 != 1]", "")
|
||||
assert.Equal(t, body["index"], 1, "")
|
||||
assert.Equal(t, body["cause"], "[ != XXX] [10 != 2]", "")
|
||||
assert.Equal(t, body["index"], 2, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -216,7 +215,7 @@ func TestV2SetKeyCASWithInvalidIndex(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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", "")
|
||||
@ -233,18 +232,17 @@ 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
||||
body := tests.ReadBodyJSON(resp)
|
||||
assert.Equal(t, body["action"], "compareAndSwap", "")
|
||||
|
||||
node := body["node"].(map[string]interface{})
|
||||
assert.Equal(t, node["prevValue"], "XXX", "")
|
||||
assert.Equal(t, node["value"], "YYY", "")
|
||||
assert.Equal(t, node["modifiedIndex"], 2, "")
|
||||
assert.Equal(t, node["modifiedIndex"], 3, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -257,16 +255,16 @@ 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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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)
|
||||
resp, _ = tests.PutForm(fmt.Sprintf("%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 != 1]", "")
|
||||
assert.Equal(t, body["index"], 1, "")
|
||||
assert.Equal(t, body["cause"], "[AAA != XXX] [0 != 2]", "")
|
||||
assert.Equal(t, body["index"], 2, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -279,7 +277,7 @@ func TestV2SetKeyCASWithMissingValueFails(t *testing.T) {
|
||||
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)
|
||||
resp, _ := tests.PutForm(fmt.Sprintf("%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", "")
|
||||
|
17
test.sh
17
test.sh
@ -1,6 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ -z "$PKG" ]; then
|
||||
PKG="./store ./server ./server/v2/tests ./mod/lock/v2/tests"
|
||||
fi
|
||||
|
||||
# Get GOPATH, etc from build
|
||||
. ./build
|
||||
|
||||
@ -8,14 +12,11 @@ set -e
|
||||
export GOPATH="${PWD}"
|
||||
|
||||
# Unit tests
|
||||
go test -i ./server
|
||||
go test -v ./server
|
||||
|
||||
go test -i ./server/v2/tests
|
||||
go test -v ./server/v2/tests
|
||||
|
||||
go test -i ./store
|
||||
go test -v ./store
|
||||
for i in $PKG
|
||||
do
|
||||
go test -i $i
|
||||
go test -v $i
|
||||
done
|
||||
|
||||
# Functional tests
|
||||
go test -i ./tests/functional
|
||||
|
@ -25,8 +25,10 @@ func RunServer(f func(*server.Server)) {
|
||||
|
||||
store := store.New()
|
||||
registry := server.NewRegistry(store)
|
||||
ps := server.NewPeerServer(testName, path, testRaftURL, testRaftURL, &server.TLSConfig{Scheme: "http"}, &server.TLSInfo{}, registry, store, testSnapshotCount, testHeartbeatTimeout, testElectionTimeout)
|
||||
s := server.New(testName, testClientURL, testClientURL, &server.TLSConfig{Scheme: "http"}, &server.TLSInfo{}, ps, registry, store)
|
||||
|
||||
ps := server.NewPeerServer(testName, path, "http://" + testRaftURL, testRaftURL, &server.TLSConfig{Scheme: "http"}, &server.TLSInfo{}, registry, store, testSnapshotCount, testHeartbeatTimeout, testElectionTimeout)
|
||||
ps.MaxClusterSize = 9
|
||||
s := server.New(testName, "http://" + testClientURL, testClientURL, &server.TLSConfig{Scheme: "http"}, &server.TLSInfo{}, ps, registry, store)
|
||||
ps.SetServer(s)
|
||||
|
||||
// Start up peer server.
|
||||
|
Loading…
x
Reference in New Issue
Block a user