Browse code

Support node label update.

Signed-off-by: Dong Chen <dongluo.chen@docker.com>

Dong Chen authored on 2016/07/01 09:33:43
Showing 6 changed files
... ...
@@ -21,8 +21,9 @@ func newAcceptCommand(dockerCli *client.DockerCli) *cobra.Command {
21 21
 }
22 22
 
23 23
 func runAccept(dockerCli *client.DockerCli, nodes []string) error {
24
-	accept := func(node *swarm.Node) {
24
+	accept := func(node *swarm.Node) error {
25 25
 		node.Spec.Membership = swarm.NodeMembershipAccepted
26
+		return nil
26 27
 	}
27 28
 	success := func(nodeID string) {
28 29
 		fmt.Fprintf(dockerCli.Out(), "Node %s accepted in the swarm.\n", nodeID)
... ...
@@ -21,8 +21,9 @@ func newDemoteCommand(dockerCli *client.DockerCli) *cobra.Command {
21 21
 }
22 22
 
23 23
 func runDemote(dockerCli *client.DockerCli, nodes []string) error {
24
-	demote := func(node *swarm.Node) {
24
+	demote := func(node *swarm.Node) error {
25 25
 		node.Spec.Role = swarm.NodeRoleWorker
26
+		return nil
26 27
 	}
27 28
 	success := func(nodeID string) {
28 29
 		fmt.Fprintf(dockerCli.Out(), "Manager %s demoted in the swarm.\n", nodeID)
... ...
@@ -4,18 +4,37 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"github.com/docker/docker/opts"
8
+	runconfigopts "github.com/docker/docker/runconfig/opts"
7 9
 	"github.com/docker/engine-api/types/swarm"
8 10
 )
9 11
 
10 12
 type nodeOptions struct {
13
+	annotations
11 14
 	role         string
12 15
 	membership   string
13 16
 	availability string
14 17
 }
15 18
 
19
+type annotations struct {
20
+	name   string
21
+	labels opts.ListOpts
22
+}
23
+
24
+func newNodeOptions() *nodeOptions {
25
+	return &nodeOptions{
26
+		annotations: annotations{
27
+			labels: opts.NewListOpts(nil),
28
+		},
29
+	}
30
+}
31
+
16 32
 func (opts *nodeOptions) ToNodeSpec() (swarm.NodeSpec, error) {
17 33
 	var spec swarm.NodeSpec
18 34
 
35
+	spec.Annotations.Name = opts.annotations.name
36
+	spec.Annotations.Labels = runconfigopts.ConvertKVStringsToMap(opts.annotations.labels.GetAll())
37
+
19 38
 	switch swarm.NodeRole(strings.ToLower(opts.role)) {
20 39
 	case swarm.NodeRoleWorker:
21 40
 		spec.Role = swarm.NodeRoleWorker
... ...
@@ -21,8 +21,9 @@ func newPromoteCommand(dockerCli *client.DockerCli) *cobra.Command {
21 21
 }
22 22
 
23 23
 func runPromote(dockerCli *client.DockerCli, nodes []string) error {
24
-	promote := func(node *swarm.Node) {
24
+	promote := func(node *swarm.Node) error {
25 25
 		node.Spec.Role = swarm.NodeRoleManager
26
+		return nil
26 27
 	}
27 28
 	success := func(nodeID string) {
28 29
 		fmt.Fprintf(dockerCli.Out(), "Node %s promoted to a manager in the swarm.\n", nodeID)
... ...
@@ -5,6 +5,8 @@ import (
5 5
 
6 6
 	"github.com/docker/docker/api/client"
7 7
 	"github.com/docker/docker/cli"
8
+	"github.com/docker/docker/opts"
9
+	runconfigopts "github.com/docker/docker/runconfig/opts"
8 10
 	"github.com/docker/engine-api/types/swarm"
9 11
 	"github.com/spf13/cobra"
10 12
 	"github.com/spf13/pflag"
... ...
@@ -12,7 +14,7 @@ import (
12 12
 )
13 13
 
14 14
 func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
15
-	var opts nodeOptions
15
+	nodeOpts := newNodeOptions()
16 16
 
17 17
 	cmd := &cobra.Command{
18 18
 		Use:   "update [OPTIONS] NODE",
... ...
@@ -24,9 +26,12 @@ func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
24 24
 	}
25 25
 
26 26
 	flags := cmd.Flags()
27
-	flags.StringVar(&opts.role, flagRole, "", "Role of the node (worker/manager)")
28
-	flags.StringVar(&opts.membership, flagMembership, "", "Membership of the node (accepted/rejected)")
29
-	flags.StringVar(&opts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
27
+	flags.StringVar(&nodeOpts.role, flagRole, "", "Role of the node (worker/manager)")
28
+	flags.StringVar(&nodeOpts.membership, flagMembership, "", "Membership of the node (accepted/rejected)")
29
+	flags.StringVar(&nodeOpts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
30
+	flags.Var(&nodeOpts.annotations.labels, flagLabelAdd, "Add or update a node label (key=value)")
31
+	labelKeys := opts.NewListOpts(nil)
32
+	flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists")
30 33
 	return cmd
31 34
 }
32 35
 
... ...
@@ -37,7 +42,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, nodeID string)
37 37
 	return updateNodes(dockerCli, []string{nodeID}, mergeNodeUpdate(flags), success)
38 38
 }
39 39
 
40
-func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(node *swarm.Node), success func(nodeID string)) error {
40
+func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(node *swarm.Node) error, success func(nodeID string)) error {
41 41
 	client := dockerCli.Client()
42 42
 	ctx := context.Background()
43 43
 
... ...
@@ -47,7 +52,10 @@ func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(nod
47 47
 			return err
48 48
 		}
49 49
 
50
-		mergeNode(&node)
50
+		err = mergeNode(&node)
51
+		if err != nil {
52
+			return err
53
+		}
51 54
 		err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec)
52 55
 		if err != nil {
53 56
 			return err
... ...
@@ -57,22 +65,51 @@ func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(nod
57 57
 	return nil
58 58
 }
59 59
 
60
-func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) {
61
-	return func(node *swarm.Node) {
60
+func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
61
+	return func(node *swarm.Node) error {
62 62
 		spec := &node.Spec
63 63
 
64 64
 		if flags.Changed(flagRole) {
65
-			str, _ := flags.GetString(flagRole)
65
+			str, err := flags.GetString(flagRole)
66
+			if err != nil {
67
+				return err
68
+			}
66 69
 			spec.Role = swarm.NodeRole(str)
67 70
 		}
68 71
 		if flags.Changed(flagMembership) {
69
-			str, _ := flags.GetString(flagMembership)
72
+			str, err := flags.GetString(flagMembership)
73
+			if err != nil {
74
+				return err
75
+			}
70 76
 			spec.Membership = swarm.NodeMembership(str)
71 77
 		}
72 78
 		if flags.Changed(flagAvailability) {
73
-			str, _ := flags.GetString(flagAvailability)
79
+			str, err := flags.GetString(flagAvailability)
80
+			if err != nil {
81
+				return err
82
+			}
74 83
 			spec.Availability = swarm.NodeAvailability(str)
75 84
 		}
85
+		if spec.Annotations.Labels == nil {
86
+			spec.Annotations.Labels = make(map[string]string)
87
+		}
88
+		if flags.Changed(flagLabelAdd) {
89
+			labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
90
+			for k, v := range runconfigopts.ConvertKVStringsToMap(labels) {
91
+				spec.Annotations.Labels[k] = v
92
+			}
93
+		}
94
+		if flags.Changed(flagLabelRemove) {
95
+			keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll()
96
+			for _, k := range keys {
97
+				// if a key doesn't exist, fail the command explicitly
98
+				if _, exists := spec.Annotations.Labels[k]; !exists {
99
+					return fmt.Errorf("key %s doesn't exist in node's labels", k)
100
+				}
101
+				delete(spec.Annotations.Labels, k)
102
+			}
103
+		}
104
+		return nil
76 105
 	}
77 106
 }
78 107
 
... ...
@@ -80,4 +117,6 @@ const (
80 80
 	flagRole         = "role"
81 81
 	flagMembership   = "membership"
82 82
 	flagAvailability = "availability"
83
+	flagLabelAdd     = "label-add"
84
+	flagLabelRemove  = "label-rm"
83 85
 )
... ...
@@ -19,6 +19,8 @@ Update a node
19 19
 Options:
20 20
       --availability string   Availability of the node (active/pause/drain)
21 21
       --help                  Print usage
22
+      --label-add value       Add or update a node label (key=value) (default [])
23
+      --label-rm value        Remove a node label if exists (default [])
22 24
       --membership string     Membership of the node (accepted/rejected)
23 25
       --role string           Role of the node (worker/manager)
24 26
 ```