diff --git a/discovery/kv/kv.go b/discovery/kv/kv.go index d36f2077b3..ef420d37f8 100644 --- a/discovery/kv/kv.go +++ b/discovery/kv/kv.go @@ -11,13 +11,17 @@ import ( "github.com/docker/swarm/pkg/store" ) +const ( + discoveryPath = "docker/swarm/nodes" +) + // Discovery is exported type Discovery struct { backend store.Backend store store.Store heartbeat time.Duration ttl time.Duration - prefix string + path string } func init() { @@ -34,23 +38,20 @@ func Init() { // Initialize is exported func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration) error { var ( - parts = strings.SplitN(uris, "/", 2) - ips = strings.Split(parts[0], ",") - addrs []string - err error + parts = strings.SplitN(uris, "/", 2) + addrs = strings.Split(parts[0], ",") + prefix = "" + err error ) - if len(parts) != 2 { - return fmt.Errorf("invalid format %q, missing ", uris) - } - - for _, ip := range ips { - addrs = append(addrs, ip) + // A custom prefix to the path can be optionally used. + if len(parts) == 2 { + prefix = parts[1] } s.heartbeat = heartbeat s.ttl = ttl - s.prefix = parts[1] + s.path = path.Join(prefix, discoveryPath) // Creates a new store, will ignore options given // if not supported by the chosen store @@ -108,7 +109,7 @@ func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-c // Will only stop if we receive a stopCh request. for { // Set up a watch. - watchCh, err := s.store.WatchTree(s.prefix, stopCh) + watchCh, err := s.store.WatchTree(s.path, stopCh) if err != nil { errCh <- err } else { @@ -129,7 +130,7 @@ func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-c // Register is exported func (s *Discovery) Register(addr string) error { opts := &store.WriteOptions{Ephemeral: true, Heartbeat: s.heartbeat} - return s.store.Put(path.Join(s.prefix, addr), []byte(addr), opts) + return s.store.Put(path.Join(s.path, addr), []byte(addr), opts) } // Store returns the underlying store used by KV discovery. diff --git a/discovery/kv/kv_test.go b/discovery/kv/kv_test.go index 0f349f9255..39d5d74063 100644 --- a/discovery/kv/kv_test.go +++ b/discovery/kv/kv_test.go @@ -2,6 +2,7 @@ package kv import ( "errors" + "path" "testing" "time" @@ -13,14 +14,18 @@ import ( func TestInitialize(t *testing.T) { d := &Discovery{backend: store.MOCK} - assert.EqualError(t, d.Initialize("127.0.0.1", 0, 0), "invalid format \"127.0.0.1\", missing ") + assert.NoError(t, d.Initialize("127.0.0.1", 0, 0)) + s := d.store.(*store.Mock) + assert.Len(t, s.Endpoints, 1) + assert.Equal(t, s.Endpoints[0], "127.0.0.1") + assert.Equal(t, d.path, discoveryPath) d = &Discovery{backend: store.MOCK} assert.NoError(t, d.Initialize("127.0.0.1:1234/path", 0, 0)) - s := d.store.(*store.Mock) + s = d.store.(*store.Mock) assert.Len(t, s.Endpoints, 1) assert.Equal(t, s.Endpoints[0], "127.0.0.1:1234") - assert.Equal(t, d.prefix, "path") + assert.Equal(t, d.path, "path/"+discoveryPath) d = &Discovery{backend: store.MOCK} assert.NoError(t, d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0)) @@ -29,7 +34,7 @@ func TestInitialize(t *testing.T) { assert.Equal(t, s.Endpoints[0], "127.0.0.1:1234") assert.Equal(t, s.Endpoints[1], "127.0.0.2:1234") assert.Equal(t, s.Endpoints[2], "127.0.0.3:1234") - assert.Equal(t, d.prefix, "path") + assert.Equal(t, d.path, "path/"+discoveryPath) } func TestWatch(t *testing.T) { @@ -40,16 +45,16 @@ func TestWatch(t *testing.T) { mockCh := make(chan []*store.KVPair) // The first watch will fail. - s.On("WatchTree", "path", mock.Anything).Return(mockCh, errors.New("test error")).Once() + s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, errors.New("test error")).Once() // The second one will succeed. - s.On("WatchTree", "path", mock.Anything).Return(mockCh, nil).Once() + s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, nil).Once() expected := discovery.Entries{ &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, } kvs := []*store.KVPair{ - {Key: "path/1.1.1.1", Value: []byte("1.1.1.1:1111")}, - {Key: "path/1.1.1.1", Value: []byte("2.2.2.2:2222")}, + {Key: path.Join("path", discoveryPath, "1.1.1.1"), Value: []byte("1.1.1.1:1111")}, + {Key: path.Join("path", discoveryPath, "2.2.2.2"), Value: []byte("2.2.2.2:2222")}, } stopCh := make(chan struct{}) @@ -69,13 +74,13 @@ func TestWatch(t *testing.T) { // Add a new entry. expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"}) - kvs = append(kvs, &store.KVPair{Key: "path/3.3.3.3", Value: []byte("3.3.3.3:3333")}) + kvs = append(kvs, &store.KVPair{Key: path.Join("path", discoveryPath, "3.3.3.3"), Value: []byte("3.3.3.3:3333")}) mockCh <- kvs assert.Equal(t, <-ch, expected) // Make sure that if an error occurs it retries. // This third call to WatchTree will be checked later by AssertExpectations. - s.On("WatchTree", "path", mock.Anything).Return(mockCh, nil) + s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, nil) close(mockCh) // Give it enough time to call WatchTree. time.Sleep(3) diff --git a/docs/discovery.md b/docs/discovery.md index 6a4d776ebc..e551e9b939 100644 --- a/docs/discovery.md +++ b/docs/discovery.md @@ -95,13 +95,13 @@ On each of your nodes, start the Swarm agent. The node IP address doesn't have to be public as long as the swarm manager can access it. ```bash -swarm join --addr= etcd:/// +swarm join --addr= etcd://,/ ``` Start the manager on any machine or your laptop. ```bash -swarm manage -H tcp:// etcd:/// +swarm manage -H tcp:// etcd://,/ ``` And then use the regular Docker commands. @@ -117,7 +117,7 @@ docker -H tcp:// logs ... You can list the nodes in your cluster. ```bash -swarm list etcd:/// +swarm list etcd://,/ ``` @@ -127,13 +127,13 @@ On each of your nodes, start the Swarm agent. The node IP address doesn't need to be public as long as the Swarm manager can access it. ```bash -swarm join --addr= consul:/// +swarm join --addr= consul:/// ``` Start the manager on any machine or your laptop. ```bash -swarm manage -H tcp:// consul:/// +swarm manage -H tcp:// consul:/// ``` And then use the regular Docker commands. @@ -149,7 +149,7 @@ docker -H tcp:// logs ... You can list the nodes in your cluster. ```bash -swarm list consul:/// +swarm list consul:/// ``` @@ -159,13 +159,13 @@ On each of your nodes, start the Swarm agent. The node IP doesn't have to be public as long as the swarm manager can access it. ```bash -swarm join --addr= zk://,/ +swarm join --addr= zk://,/ ``` Start the manager on any machine or your laptop. ```bash -swarm manage -H tcp:// zk://,/ +swarm manage -H tcp:// zk://,/ ``` You can then use the regular Docker commands. @@ -181,7 +181,7 @@ docker -H tcp:// logs ... You can list the nodes in the cluster. ```bash -swarm list zk://,/ +swarm list zk://,/ ```