mirror of https://github.com/docker/docs.git
store: Add unit tests for pkg/store and Consul/Zookeeper/Etcd
Signed-off-by: Alexandre Beslic <abronan@docker.com>
This commit is contained in:
parent
7e4cde9d08
commit
18cfe74d5b
|
@ -19,7 +19,15 @@ install:
|
||||||
- go get github.com/golang/lint/golint
|
- go get github.com/golang/lint/golint
|
||||||
- go get github.com/GeertJohan/fgt
|
- go get github.com/GeertJohan/fgt
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- script/travis_consul.sh 0.5.2
|
||||||
|
- script/travis_etcd.sh 2.0.11
|
||||||
|
- script/travis_zk.sh 3.4.6
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- ./consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -config-file=./config.json 1>/dev/null &
|
||||||
|
- ./etcd/etcd --listen-client-urls 'http://0.0.0.0:4001' --advertise-client-urls 'http://127.0.0.1:4001' >/dev/null 2>&1 &
|
||||||
|
- ./zk/bin/zkServer.sh start ./zk/conf/zoo.cfg 1> /dev/null
|
||||||
- script/validate-gofmt
|
- script/validate-gofmt
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
- fgt golint ./...
|
- fgt golint ./...
|
||||||
|
|
|
@ -16,10 +16,6 @@ const (
|
||||||
// watched key has changed. This affects the minimum time it takes to
|
// watched key has changed. This affects the minimum time it takes to
|
||||||
// cancel a watch.
|
// cancel a watch.
|
||||||
DefaultWatchWaitTime = 15 * time.Second
|
DefaultWatchWaitTime = 15 * time.Second
|
||||||
|
|
||||||
// MinimumTimeToLive is the minimum TTL value allowed by Consul for
|
|
||||||
// Ephemeral entries
|
|
||||||
MinimumTimeToLive = 10 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Consul embeds the client and watches
|
// Consul embeds the client and watches
|
||||||
|
@ -56,9 +52,7 @@ func InitializeConsul(endpoints []string, options *Config) (Store, error) {
|
||||||
s.setTimeout(options.ConnectionTimeout)
|
s.setTimeout(options.ConnectionTimeout)
|
||||||
}
|
}
|
||||||
if options.EphemeralTTL != 0 {
|
if options.EphemeralTTL != 0 {
|
||||||
if err := s.setEphemeralTTL(options.EphemeralTTL); err != nil {
|
s.setEphemeralTTL(options.EphemeralTTL)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +81,8 @@ func (s *Consul) setTimeout(time time.Duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEphemeralTTL sets the ttl for ephemeral nodes
|
// SetEphemeralTTL sets the ttl for ephemeral nodes
|
||||||
func (s *Consul) setEphemeralTTL(ttl time.Duration) error {
|
func (s *Consul) setEphemeralTTL(ttl time.Duration) {
|
||||||
if ttl < MinimumTimeToLive {
|
|
||||||
return ErrInvalidTTL
|
|
||||||
}
|
|
||||||
s.ephemeralTTL = ttl
|
s.ephemeralTTL = ttl
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createEphemeralSession creates the global session
|
// createEphemeralSession creates the global session
|
||||||
|
|
|
@ -0,0 +1,410 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeConsulClient(t *testing.T) Store {
|
||||||
|
client := "localhost:8500"
|
||||||
|
|
||||||
|
kv, err := NewStore(
|
||||||
|
CONSUL,
|
||||||
|
[]string{client},
|
||||||
|
&Config{
|
||||||
|
ConnectionTimeout: 10 * time.Second,
|
||||||
|
EphemeralTTL: 2 * time.Second,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulPutGetDelete(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete the key
|
||||||
|
err = kv.Delete(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulWatch(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
key := "hello"
|
||||||
|
value := []byte("world")
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.Watch(key, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
err := kv.Put(key, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
if eventCount == 1 {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, value)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, newValue)
|
||||||
|
}
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulWatchTree(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
dir := "tree"
|
||||||
|
|
||||||
|
node1 := "tree/node1"
|
||||||
|
value1 := []byte("node1")
|
||||||
|
|
||||||
|
node2 := "tree/node2"
|
||||||
|
value2 := []byte("node2")
|
||||||
|
|
||||||
|
node3 := "tree/node3"
|
||||||
|
value3 := []byte("node3")
|
||||||
|
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
err := kv.Put(node1, value1, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node2, value2, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node3, value3, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.WatchTree(dir, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
err := kv.Put(node1, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
assert.Equal(t, len(event), 3)
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulAtomicPut(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
key := "hello"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// This CAS should succeed
|
||||||
|
success, _, err := kv.AtomicPut("hello", []byte("WORLD"), pair, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
|
||||||
|
// This CAS should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, _, err = kv.AtomicPut("hello", []byte("WORLDWORLD"), pair, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulAtomicDelete(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
key := "atomic"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
tempIndex := pair.LastIndex
|
||||||
|
|
||||||
|
// AtomicDelete should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, err := kv.AtomicDelete(key, pair)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
|
||||||
|
// AtomicDelete should succeed
|
||||||
|
pair.LastIndex = tempIndex
|
||||||
|
success, err = kv.AtomicDelete(key, pair)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulLockUnlock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// We should be able to create a new lock on key
|
||||||
|
lock, err := kv.NewLock(key, &LockOptions{Value: value})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, lock)
|
||||||
|
|
||||||
|
// Lock should successfully succeed or block
|
||||||
|
lockChan, err := lock.Lock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, lockChan)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Unlock should succeed
|
||||||
|
err = lock.Unlock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulPutEphemeral(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
firstKey := "foo"
|
||||||
|
firstValue := []byte("foo")
|
||||||
|
|
||||||
|
secondKey := "bar"
|
||||||
|
secondValue := []byte("bar")
|
||||||
|
|
||||||
|
// Put the first key with the Ephemeral flag
|
||||||
|
err := kv.Put(firstKey, firstValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put a second key with the Ephemeral flag
|
||||||
|
err = kv.Put(secondKey, secondValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get on firstKey should work
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey should work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Let the session expire
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
|
||||||
|
// Get on firstKey shouldn't work
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey shouldn't work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulList(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
prefix := "nodes/"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// List should work and return the two correct values
|
||||||
|
pairs, err := kv.List(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pairs) {
|
||||||
|
assert.Equal(t, len(pairs), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pairs, those are not necessarily in Put order
|
||||||
|
for _, pair := range pairs {
|
||||||
|
if pair.Key == firstKey {
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
}
|
||||||
|
if pair.Key == secondKey {
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConsulDeleteTree(t *testing.T) {
|
||||||
|
kv := makeConsulClient(t)
|
||||||
|
|
||||||
|
prefix := "nodes/"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work on the first Key
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Get should work on the second Key
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete Values under directory `nodes`
|
||||||
|
err = kv.DeleteTree(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail on both keys
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
|
@ -0,0 +1,412 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeEtcdClient(t *testing.T) Store {
|
||||||
|
client := "localhost:4001"
|
||||||
|
|
||||||
|
kv, err := NewStore(
|
||||||
|
ETCD,
|
||||||
|
[]string{client},
|
||||||
|
&Config{
|
||||||
|
ConnectionTimeout: 10 * time.Second,
|
||||||
|
EphemeralTTL: 2 * time.Second,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdPutGetDelete(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete the key
|
||||||
|
err = kv.Delete(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdWatch(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
// FIXME General key formatting issue
|
||||||
|
key := "/hello"
|
||||||
|
value := []byte("world")
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.Watch(key, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
err := kv.Put(key, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
if eventCount == 1 {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, value)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, newValue)
|
||||||
|
}
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdWatchTree(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
// FIXME General key formatting issue
|
||||||
|
dir := "/tree"
|
||||||
|
|
||||||
|
node1 := "tree/node1"
|
||||||
|
value1 := []byte("node1")
|
||||||
|
|
||||||
|
node2 := "tree/node2"
|
||||||
|
value2 := []byte("node2")
|
||||||
|
|
||||||
|
node3 := "tree/node3"
|
||||||
|
value3 := []byte("node3")
|
||||||
|
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
err := kv.Put(node1, value1, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node2, value2, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node3, value3, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.WatchTree(dir, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
err := kv.Put(node1, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
assert.Equal(t, len(event), 3)
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdAtomicPut(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
key := "hello"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// This CAS should succeed
|
||||||
|
success, _, err := kv.AtomicPut("hello", []byte("WORLD"), pair, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
|
||||||
|
// This CAS should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, _, err = kv.AtomicPut("hello", []byte("WORLDWORLD"), pair, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdAtomicDelete(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
key := "atomic"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
tempIndex := pair.LastIndex
|
||||||
|
|
||||||
|
// AtomicDelete should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, err := kv.AtomicDelete(key, pair)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
|
||||||
|
// AtomicDelete should succeed
|
||||||
|
pair.LastIndex = tempIndex
|
||||||
|
success, err = kv.AtomicDelete(key, pair)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdLockUnlock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// We should be able to create a new lock on key
|
||||||
|
lock, err := kv.NewLock(key, &LockOptions{Value: value})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, lock)
|
||||||
|
|
||||||
|
// Lock should successfully succeed or block
|
||||||
|
lockChan, err := lock.Lock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, lockChan)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Unlock should succeed
|
||||||
|
err = lock.Unlock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdPutEphemeral(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
firstKey := "foo"
|
||||||
|
firstValue := []byte("foo")
|
||||||
|
|
||||||
|
secondKey := "bar"
|
||||||
|
secondValue := []byte("bar")
|
||||||
|
|
||||||
|
// Put the first key with the Ephemeral flag
|
||||||
|
err := kv.Put(firstKey, firstValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put a second key with the Ephemeral flag
|
||||||
|
err = kv.Put(secondKey, secondValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get on firstKey should work
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey should work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Let the session expire
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
// Get on firstKey shouldn't work
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey shouldn't work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdList(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
prefix := "nodes/"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// List should work and return the two correct values
|
||||||
|
pairs, err := kv.List(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pairs) {
|
||||||
|
assert.Equal(t, len(pairs), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pairs, those are not necessarily in Put order
|
||||||
|
for _, pair := range pairs {
|
||||||
|
if pair.Key == firstKey {
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
}
|
||||||
|
if pair.Key == secondKey {
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdDeleteTree(t *testing.T) {
|
||||||
|
kv := makeEtcdClient(t)
|
||||||
|
|
||||||
|
prefix := "nodes/"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work on the first Key
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Get should work on the second Key
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete Values under directory `nodes`
|
||||||
|
err = kv.DeleteTree(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail on both keys
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
|
@ -0,0 +1,421 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeZkClient(t *testing.T) Store {
|
||||||
|
client := "localhost:2181"
|
||||||
|
|
||||||
|
kv, err := NewStore(
|
||||||
|
ZK,
|
||||||
|
[]string{client},
|
||||||
|
&Config{
|
||||||
|
ConnectionTimeout: 10 * time.Second,
|
||||||
|
EphemeralTTL: 2 * time.Second,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkPutGetDelete(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete the key
|
||||||
|
err = kv.Delete(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkWatch(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
key := "hello"
|
||||||
|
value := []byte("world")
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.Watch(key, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
err := kv.Put(key, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
if eventCount == 1 {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, value)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, event.Key, key)
|
||||||
|
assert.Equal(t, event.Value, newValue)
|
||||||
|
}
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkWatchTree(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
dir := "tree"
|
||||||
|
|
||||||
|
node1 := "tree/node1"
|
||||||
|
value1 := []byte("node1")
|
||||||
|
|
||||||
|
node2 := "tree/node2"
|
||||||
|
value2 := []byte("node2")
|
||||||
|
|
||||||
|
node3 := "tree/node3"
|
||||||
|
value3 := []byte("node3")
|
||||||
|
|
||||||
|
newValue := []byte("world!")
|
||||||
|
|
||||||
|
err := kv.Put(node1, value1, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node2, value2, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = kv.Put(node3, value3, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
stopCh := make(<-chan struct{})
|
||||||
|
events, err := kv.WatchTree(dir, stopCh)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, events)
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
go func() {
|
||||||
|
timeout := time.After(750 * time.Millisecond)
|
||||||
|
tick := time.Tick(250 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case <-tick:
|
||||||
|
// Zookeeper special case, only added and deleted
|
||||||
|
// nodes triggers the ChildrenW
|
||||||
|
err := kv.Put(node1, newValue, nil)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
err = kv.Delete(node1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
eventCount := 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
assert.NotNil(t, event)
|
||||||
|
eventCount++
|
||||||
|
// We received all the events we wanted to check
|
||||||
|
if eventCount >= 3 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkAtomicPut(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
key := "hello"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// This CAS should succeed
|
||||||
|
success, _, err := kv.AtomicPut("hello", []byte("WORLD"), pair, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
|
||||||
|
// This CAS should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, _, err = kv.AtomicPut("hello", []byte("WORLDWORLD"), pair, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkAtomicDelete(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
key := "atomic"
|
||||||
|
value := []byte("world")
|
||||||
|
|
||||||
|
// Put the key
|
||||||
|
err := kv.Put(key, value, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should return the value and an incremented index
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
tempIndex := pair.LastIndex
|
||||||
|
|
||||||
|
// AtomicDelete should fail
|
||||||
|
pair.LastIndex = 0
|
||||||
|
success, err := kv.AtomicDelete(key, pair)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.False(t, success)
|
||||||
|
|
||||||
|
// AtomicDelete should succeed
|
||||||
|
pair.LastIndex = tempIndex
|
||||||
|
success, err = kv.AtomicDelete(key, pair)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkLockUnlock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
key := "foo"
|
||||||
|
value := []byte("bar")
|
||||||
|
|
||||||
|
// We should be able to create a new lock on key
|
||||||
|
lock, err := kv.NewLock(key, &LockOptions{Value: value})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, lock)
|
||||||
|
|
||||||
|
// Lock should successfully succeed or block
|
||||||
|
lockChan, err := lock.Lock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// FIXME zookeeper specific chan is nil
|
||||||
|
assert.Nil(t, lockChan)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err := kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Unlock should succeed
|
||||||
|
err = lock.Unlock()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
pair, err = kv.Get(key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, value)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkPutEphemeral(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
firstKey := "foo"
|
||||||
|
firstValue := []byte("foo")
|
||||||
|
|
||||||
|
secondKey := "bar"
|
||||||
|
secondValue := []byte("bar")
|
||||||
|
|
||||||
|
// Put the first key with the Ephemeral flag
|
||||||
|
err := kv.Put(firstKey, firstValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put a second key with the Ephemeral flag
|
||||||
|
err = kv.Put(secondKey, secondValue, &WriteOptions{Ephemeral: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get on firstKey should work
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey should work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, pair)
|
||||||
|
|
||||||
|
// Close Zookeeper's client connection
|
||||||
|
zk := kv.(*Zookeeper)
|
||||||
|
zk.client.Close()
|
||||||
|
|
||||||
|
// Let the session expire
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
kv = makeZkClient(t)
|
||||||
|
|
||||||
|
// Get on firstKey shouldn't work
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
// Get on secondKey shouldn't work
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkList(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
// FIXME key parsing with last `/`
|
||||||
|
prefix := "nodes"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// List should work and return the two correct values
|
||||||
|
pairs, err := kv.List(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pairs) {
|
||||||
|
assert.Equal(t, len(pairs), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pairs, those are not necessarily in Put order
|
||||||
|
for _, pair := range pairs {
|
||||||
|
if pair.Key == firstKey {
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
}
|
||||||
|
if pair.Key == secondKey {
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZkDeleteTree(t *testing.T) {
|
||||||
|
kv := makeZkClient(t)
|
||||||
|
|
||||||
|
// FIXME key parsing with last `/`
|
||||||
|
prefix := "nodes"
|
||||||
|
|
||||||
|
firstKey := "nodes/first"
|
||||||
|
firstValue := []byte("first")
|
||||||
|
|
||||||
|
secondKey := "nodes/second"
|
||||||
|
secondValue := []byte("second")
|
||||||
|
|
||||||
|
// Put the first key
|
||||||
|
err := kv.Put(firstKey, firstValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Put the second key
|
||||||
|
err = kv.Put(secondKey, secondValue, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should work on the first Key
|
||||||
|
pair, err := kv.Get(firstKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, firstValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Get should work on the second Key
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, pair) {
|
||||||
|
assert.NotNil(t, pair.Value)
|
||||||
|
}
|
||||||
|
assert.Equal(t, pair.Value, secondValue)
|
||||||
|
assert.NotEqual(t, pair.LastIndex, 0)
|
||||||
|
|
||||||
|
// Delete Values under directory `nodes`
|
||||||
|
err = kv.DeleteTree(prefix)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get should fail on both keys
|
||||||
|
pair, err = kv.Get(firstKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
|
||||||
|
pair, err = kv.Get(secondKey)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, pair)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ $# -gt 0 ] ; then
|
||||||
|
CONSUL_VERSION="$1"
|
||||||
|
else
|
||||||
|
CONSUL_VERSION="0.5.2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# install consul
|
||||||
|
wget "https://dl.bintray.com/mitchellh/consul/${CONSUL_VERSION}_linux_amd64.zip"
|
||||||
|
unzip "${CONSUL_VERSION}_linux_amd64.zip"
|
||||||
|
|
||||||
|
# make config for minimum ttl
|
||||||
|
touch config.json
|
||||||
|
echo "{\"session_ttl_min\": \"2s\"}" >> config.json
|
||||||
|
|
||||||
|
# check
|
||||||
|
./consul --version
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ $# -gt 0 ] ; then
|
||||||
|
ETCD_VERSION="$1"
|
||||||
|
else
|
||||||
|
ETCD_VERSION="2.0.11"
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -L https://github.com/coreos/etcd/releases/download/v$ETCD_VERSION/etcd-v$ETCD_VERSION-linux-amd64.tar.gz -o etcd-v$ETCD_VERSION-linux-amd64.tar.gz
|
||||||
|
tar xzvf etcd-v$ETCD_VERSION-linux-amd64.tar.gz
|
||||||
|
mv etcd-v$ETCD_VERSION-linux-amd64 etcd
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ $# -gt 0 ] ; then
|
||||||
|
ZK_VERSION="$1"
|
||||||
|
else
|
||||||
|
ZK_VERSION="3.4.6"
|
||||||
|
fi
|
||||||
|
|
||||||
|
wget "http://mirrors.ukfast.co.uk/sites/ftp.apache.org/zookeeper/stable/zookeeper-${ZK_VERSION}.tar.gz"
|
||||||
|
tar -xvf "zookeeper-${ZK_VERSION}.tar.gz"
|
||||||
|
mv zookeeper-$ZK_VERSION zk
|
||||||
|
mv ./zk/conf/zoo_sample.cfg ./zk/conf/zoo.cfg
|
Loading…
Reference in New Issue