This fix adds a new flag `--availability` to `swarm join`.
Related documentation has been updated.
An integration test has been added.
NOTE: Additional pull request for swarmkit and engine-api will
be created separately.
This fix fixes 24596.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| ... | ... |
@@ -2,12 +2,15 @@ package swarm |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "golang.org/x/net/context" |
|
| 5 | 8 |
|
| 6 | 9 |
"github.com/docker/docker/api/types/swarm" |
| 7 | 10 |
"github.com/docker/docker/cli" |
| 8 | 11 |
"github.com/docker/docker/cli/command" |
| 9 | 12 |
"github.com/spf13/cobra" |
| 10 |
- "golang.org/x/net/context" |
|
| 13 |
+ "github.com/spf13/pflag" |
|
| 11 | 14 |
) |
| 12 | 15 |
|
| 13 | 16 |
type joinOptions struct {
|
| ... | ... |
@@ -16,6 +19,7 @@ type joinOptions struct {
|
| 16 | 16 |
// Not a NodeAddrOption because it has no default port. |
| 17 | 17 |
advertiseAddr string |
| 18 | 18 |
token string |
| 19 |
+ availability string |
|
| 19 | 20 |
} |
| 20 | 21 |
|
| 21 | 22 |
func newJoinCommand(dockerCli command.Cli) *cobra.Command {
|
| ... | ... |
@@ -29,7 +33,7 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command {
|
| 29 | 29 |
Args: cli.ExactArgs(1), |
| 30 | 30 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 31 | 31 |
opts.remote = args[0] |
| 32 |
- return runJoin(dockerCli, opts) |
|
| 32 |
+ return runJoin(dockerCli, cmd.Flags(), opts) |
|
| 33 | 33 |
}, |
| 34 | 34 |
} |
| 35 | 35 |
|
| ... | ... |
@@ -37,10 +41,11 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command {
|
| 37 | 37 |
flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: <ip|interface>[:port])") |
| 38 | 38 |
flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: <ip|interface>[:port])") |
| 39 | 39 |
flags.StringVar(&opts.token, flagToken, "", "Token for entry into the swarm") |
| 40 |
+ flags.StringVar(&opts.availability, flagAvailability, "active", "Availability of the node (active/pause/drain)") |
|
| 40 | 41 |
return cmd |
| 41 | 42 |
} |
| 42 | 43 |
|
| 43 |
-func runJoin(dockerCli command.Cli, opts joinOptions) error {
|
|
| 44 |
+func runJoin(dockerCli command.Cli, flags *pflag.FlagSet, opts joinOptions) error {
|
|
| 44 | 45 |
client := dockerCli.Client() |
| 45 | 46 |
ctx := context.Background() |
| 46 | 47 |
|
| ... | ... |
@@ -50,6 +55,16 @@ func runJoin(dockerCli command.Cli, opts joinOptions) error {
|
| 50 | 50 |
AdvertiseAddr: opts.advertiseAddr, |
| 51 | 51 |
RemoteAddrs: []string{opts.remote},
|
| 52 | 52 |
} |
| 53 |
+ if flags.Changed(flagAvailability) {
|
|
| 54 |
+ availability := swarm.NodeAvailability(strings.ToLower(opts.availability)) |
|
| 55 |
+ switch availability {
|
|
| 56 |
+ case swarm.NodeAvailabilityActive, swarm.NodeAvailabilityPause, swarm.NodeAvailabilityDrain: |
|
| 57 |
+ req.Availability = availability |
|
| 58 |
+ default: |
|
| 59 |
+ return fmt.Errorf("invalid availability %q, only active, pause and drain are supported", opts.availability)
|
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 53 | 63 |
err := client.SwarmJoin(ctx, req) |
| 54 | 64 |
if err != nil {
|
| 55 | 65 |
return err |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package cluster |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"path/filepath" |
| 5 | 6 |
"runtime" |
| 6 | 7 |
"strings" |
| ... | ... |
@@ -51,6 +52,7 @@ type nodeStartConfig struct {
|
| 51 | 51 |
joinToken string |
| 52 | 52 |
lockKey []byte |
| 53 | 53 |
autolock bool |
| 54 |
+ availability types.NodeAvailability |
|
| 54 | 55 |
} |
| 55 | 56 |
|
| 56 | 57 |
func (n *nodeRunner) Ready() chan error {
|
| ... | ... |
@@ -92,7 +94,7 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
|
| 92 | 92 |
control = filepath.Join(n.cluster.runtimeRoot, controlSocket) |
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 |
- node, err := swarmnode.New(&swarmnode.Config{
|
|
| 95 |
+ swarmnodeConfig := swarmnode.Config{
|
|
| 96 | 96 |
Hostname: n.cluster.config.Name, |
| 97 | 97 |
ForceNewCluster: conf.forceNewCluster, |
| 98 | 98 |
ListenControlAPI: control, |
| ... | ... |
@@ -106,7 +108,15 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
|
| 106 | 106 |
ElectionTick: 3, |
| 107 | 107 |
UnlockKey: conf.lockKey, |
| 108 | 108 |
AutoLockManagers: conf.autolock, |
| 109 |
- }) |
|
| 109 |
+ } |
|
| 110 |
+ if conf.availability != "" {
|
|
| 111 |
+ avail, ok := swarmapi.NodeSpec_Availability_value[strings.ToUpper(string(conf.availability))] |
|
| 112 |
+ if !ok {
|
|
| 113 |
+ return fmt.Errorf("invalid Availability: %q", conf.availability)
|
|
| 114 |
+ } |
|
| 115 |
+ swarmnodeConfig.Availability = swarmapi.NodeSpec_Availability(avail) |
|
| 116 |
+ } |
|
| 117 |
+ node, err := swarmnode.New(&swarmnodeConfig) |
|
| 110 | 118 |
if err != nil {
|
| 111 | 119 |
return err |
| 112 | 120 |
} |
| ... | ... |
@@ -22,6 +22,7 @@ Join a swarm as a node and/or manager |
| 22 | 22 |
|
| 23 | 23 |
Options: |
| 24 | 24 |
--advertise-addr string Advertised address (format: <ip|interface>[:port]) |
| 25 |
+ --availability string Availability of the node (active/pause/drain) (default "active") |
|
| 25 | 26 |
--help Print usage |
| 26 | 27 |
--listen-addr node-addr Listen address (format: <ip|interface>[:port]) (default 0.0.0.0:2377) |
| 27 | 28 |
--token string Token for entry into the swarm |
| ... | ... |
@@ -94,6 +95,15 @@ This flag is generally not necessary when joining an existing swarm. |
| 94 | 94 |
|
| 95 | 95 |
Secret value required for nodes to join the swarm |
| 96 | 96 |
|
| 97 |
+### `--availability` |
|
| 98 |
+ |
|
| 99 |
+This flag specifies the availability of the node at the time the node joins a master. |
|
| 100 |
+Possible availability values are `active`, `pause`, or `drain`. |
|
| 101 |
+ |
|
| 102 |
+This flag is useful in certain situations. For example, a cluster may want to have |
|
| 103 |
+dedicated manager nodes that are not served as worker nodes. This could be achieved |
|
| 104 |
+by passing `--availability=drain` to `docker swarm join`. |
|
| 105 |
+ |
|
| 97 | 106 |
|
| 98 | 107 |
## Related information |
| 99 | 108 |
|
| ... | ... |
@@ -1591,3 +1591,31 @@ func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *check.C) {
|
| 1591 | 1591 |
c.Assert(out, checker.Contains, "{ tcp 80 5000 ingress}")
|
| 1592 | 1592 |
c.Assert(out, checker.Contains, "{ tcp 80 5001 ingress}")
|
| 1593 | 1593 |
} |
| 1594 |
+ |
|
| 1595 |
+func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *check.C) {
|
|
| 1596 |
+ d := s.AddDaemon(c, true, true) |
|
| 1597 |
+ |
|
| 1598 |
+ out, err := d.Cmd("node", "ls")
|
|
| 1599 |
+ c.Assert(err, checker.IsNil) |
|
| 1600 |
+ c.Assert(out, checker.Not(checker.Contains), "Drain") |
|
| 1601 |
+ |
|
| 1602 |
+ out, err = d.Cmd("swarm", "join-token", "-q", "manager")
|
|
| 1603 |
+ c.Assert(err, checker.IsNil) |
|
| 1604 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 1605 |
+ |
|
| 1606 |
+ token := strings.TrimSpace(out) |
|
| 1607 |
+ |
|
| 1608 |
+ d1 := s.AddDaemon(c, false, false) |
|
| 1609 |
+ |
|
| 1610 |
+ out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.ListenAddr)
|
|
| 1611 |
+ c.Assert(err, checker.IsNil) |
|
| 1612 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 1613 |
+ |
|
| 1614 |
+ out, err = d.Cmd("node", "ls")
|
|
| 1615 |
+ c.Assert(err, checker.IsNil) |
|
| 1616 |
+ c.Assert(out, checker.Contains, "Drain") |
|
| 1617 |
+ |
|
| 1618 |
+ out, err = d1.Cmd("node", "ls")
|
|
| 1619 |
+ c.Assert(err, checker.IsNil) |
|
| 1620 |
+ c.Assert(out, checker.Contains, "Drain") |
|
| 1621 |
+} |