diff --git a/api/client/swarm/init.go b/api/client/swarm/init.go index 1f403ae4a1..69d26852c5 100644 --- a/api/client/swarm/init.go +++ b/api/client/swarm/init.go @@ -22,7 +22,7 @@ type initOptions struct { func newInitCommand(dockerCli *client.DockerCli) *cobra.Command { var flags *pflag.FlagSet opts := initOptions{ - listenAddr: NewNodeAddrOption(), + listenAddr: NewListenAddrOption(), autoAccept: NewAutoAcceptOption(), } @@ -35,10 +35,10 @@ func newInitCommand(dockerCli *client.DockerCli) *cobra.Command { }, } - flags = cmd.Flags() - flags.Var(&opts.listenAddr, "listen-addr", "Listen address") - flags.Var(&opts.autoAccept, "auto-accept", "Auto acceptance policy (worker, manager, or none)") - flags.StringVar(&opts.secret, "secret", "", "Set secret value needed to accept nodes into cluster") + flags := cmd.Flags() + flags.Var(&opts.listenAddr, flagListenAddr, "Listen address") + flags.Var(&opts.autoAccept, flagAutoAccept, "Auto acceptance policy (worker, manager, or none)") + flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to accept nodes into cluster") flags.BoolVar(&opts.forceNewCluster, "force-new-cluster", false, "Force create a new cluster from current state.") return cmd } diff --git a/api/client/swarm/join.go b/api/client/swarm/join.go index 1bb9ae04b3..e9e0f79d50 100644 --- a/api/client/swarm/join.go +++ b/api/client/swarm/join.go @@ -20,7 +20,7 @@ type joinOptions struct { func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command { opts := joinOptions{ - listenAddr: NodeAddrOption{addr: defaultListenAddr}, + listenAddr: NewListenAddrOption(), } cmd := &cobra.Command{ @@ -34,7 +34,7 @@ func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command { } flags := cmd.Flags() - flags.Var(&opts.listenAddr, "listen-addr", "Listen address") + flags.Var(&opts.listenAddr, flagListenAddr, "Listen address") flags.BoolVar(&opts.manager, "manager", false, "Try joining as a manager.") flags.StringVar(&opts.secret, "secret", "", "Secret for node acceptance") flags.StringVar(&opts.CACertHash, "ca-hash", "", "Hash of the Root Certificate Authority certificate used for trusted join") diff --git a/api/client/swarm/opts.go b/api/client/swarm/opts.go index 15129100f0..fafda92580 100644 --- a/api/client/swarm/opts.go +++ b/api/client/swarm/opts.go @@ -2,17 +2,27 @@ package swarm import ( "fmt" + "net" + "strconv" "strings" "github.com/docker/engine-api/types/swarm" ) const ( - defaultListenAddr = "0.0.0.0:2377" + defaultListenAddr = "0.0.0.0" + defaultListenPort uint16 = 2377 // WORKER constant for worker name WORKER = "WORKER" // MANAGER constant for manager name MANAGER = "MANAGER" + + flagAutoAccept = "auto-accept" + flagCertExpiry = "cert-expiry" + flagDispatcherHeartbeat = "dispatcher-heartbeat" + flagListenAddr = "listen-addr" + flagSecret = "secret" + flagTaskHistoryLimit = "task-history-limit" ) var ( @@ -25,25 +35,35 @@ var ( // NodeAddrOption is a pflag.Value for listen and remote addresses type NodeAddrOption struct { addr string + port uint16 } // String prints the representation of this flag func (a *NodeAddrOption) String() string { - return a.addr + return a.Value() } // Set the value for this flag func (a *NodeAddrOption) Set(value string) error { if !strings.Contains(value, ":") { - return fmt.Errorf("Invalid url, a host and port are required") + a.addr = value + return nil } - parts := strings.Split(value, ":") - if len(parts) != 2 { - return fmt.Errorf("Invalid url, too many colons") + host, port, err := net.SplitHostPort(value) + if err != nil { + return fmt.Errorf("Invalid url, %v", err) } - a.addr = value + portInt, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return fmt.Errorf("invalid url, %v", err) + } + a.port = uint16(portInt) + + if host != "" { + a.addr = host + } return nil } @@ -52,9 +72,19 @@ func (a *NodeAddrOption) Type() string { return "node-addr" } +// Value returns the value of this option as addr:port +func (a *NodeAddrOption) Value() string { + return net.JoinHostPort(a.addr, strconv.Itoa(int(a.port))) +} + // NewNodeAddrOption returns a new node address option -func NewNodeAddrOption() NodeAddrOption { - return NodeAddrOption{addr: defaultListenAddr} +func NewNodeAddrOption(host string, port uint16) NodeAddrOption { + return NodeAddrOption{addr: host, port: port} +} + +// NewListenAddrOption returns a NodeAddrOption with default values +func NewListenAddrOption() NodeAddrOption { + return NewNodeAddrOption(defaultListenAddr, defaultListenPort) } // AutoAcceptOption is a value type for auto-accept policy diff --git a/api/client/swarm/opts_test.go b/api/client/swarm/opts_test.go new file mode 100644 index 0000000000..d5b73575f4 --- /dev/null +++ b/api/client/swarm/opts_test.go @@ -0,0 +1,35 @@ +package swarm + +import ( + "testing" + + "github.com/docker/docker/pkg/testutil/assert" +) + +func TestNodeAddrOptionSetHostAndPort(t *testing.T) { + opt := NewNodeAddrOption("old", 123) + addr := "newhost:5555" + assert.NilError(t, opt.Set(addr)) + assert.Equal(t, opt.addr, "newhost") + assert.Equal(t, opt.port, uint16(5555)) + assert.Equal(t, opt.Value(), addr) +} + +func TestNodeAddrOptionSetHostOnly(t *testing.T) { + opt := NewListenAddrOption() + assert.NilError(t, opt.Set("newhost")) + assert.Equal(t, opt.addr, "newhost") + assert.Equal(t, opt.port, defaultListenPort) +} + +func TestNodeAddrOptionSetPortOnly(t *testing.T) { + opt := NewListenAddrOption() + assert.NilError(t, opt.Set(":4545")) + assert.Equal(t, opt.addr, defaultListenAddr) + assert.Equal(t, opt.port, uint16(4545)) +} + +func TestNodeAddrOptionSetInvalidFormat(t *testing.T) { + opt := NewListenAddrOption() + assert.Error(t, opt.Set("http://localhost:4545"), "Invalid url") +}