github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/node/update.go (about) 1 package node 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/docker/cli/cli" 8 "github.com/docker/cli/cli/command" 9 "github.com/docker/cli/opts" 10 "github.com/docker/docker/api/types/swarm" 11 "github.com/pkg/errors" 12 "github.com/spf13/cobra" 13 "github.com/spf13/pflag" 14 ) 15 16 var ( 17 errNoRoleChange = errors.New("role was already set to the requested value") 18 ) 19 20 func newUpdateCommand(dockerCli command.Cli) *cobra.Command { 21 options := newNodeOptions() 22 23 cmd := &cobra.Command{ 24 Use: "update [OPTIONS] NODE", 25 Short: "Update a node", 26 Args: cli.ExactArgs(1), 27 RunE: func(cmd *cobra.Command, args []string) error { 28 return runUpdate(dockerCli, cmd.Flags(), args[0]) 29 }, 30 } 31 32 flags := cmd.Flags() 33 flags.StringVar(&options.role, flagRole, "", `Role of the node ("worker"|"manager")`) 34 flags.StringVar(&options.availability, flagAvailability, "", `Availability of the node ("active"|"pause"|"drain")`) 35 flags.Var(&options.annotations.labels, flagLabelAdd, "Add or update a node label (key=value)") 36 labelKeys := opts.NewListOpts(nil) 37 flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists") 38 return cmd 39 } 40 41 func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, nodeID string) error { 42 success := func(_ string) { 43 fmt.Fprintln(dockerCli.Out(), nodeID) 44 } 45 return updateNodes(dockerCli, []string{nodeID}, mergeNodeUpdate(flags), success) 46 } 47 48 func updateNodes(dockerCli command.Cli, nodes []string, mergeNode func(node *swarm.Node) error, success func(nodeID string)) error { 49 client := dockerCli.Client() 50 ctx := context.Background() 51 52 for _, nodeID := range nodes { 53 node, _, err := client.NodeInspectWithRaw(ctx, nodeID) 54 if err != nil { 55 return err 56 } 57 58 err = mergeNode(&node) 59 if err != nil { 60 if err == errNoRoleChange { 61 continue 62 } 63 return err 64 } 65 err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec) 66 if err != nil { 67 return err 68 } 69 success(nodeID) 70 } 71 return nil 72 } 73 74 func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error { 75 return func(node *swarm.Node) error { 76 spec := &node.Spec 77 78 if flags.Changed(flagRole) { 79 str, err := flags.GetString(flagRole) 80 if err != nil { 81 return err 82 } 83 spec.Role = swarm.NodeRole(str) 84 } 85 if flags.Changed(flagAvailability) { 86 str, err := flags.GetString(flagAvailability) 87 if err != nil { 88 return err 89 } 90 spec.Availability = swarm.NodeAvailability(str) 91 } 92 if spec.Annotations.Labels == nil { 93 spec.Annotations.Labels = make(map[string]string) 94 } 95 if flags.Changed(flagLabelAdd) { 96 labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll() 97 for k, v := range opts.ConvertKVStringsToMap(labels) { 98 spec.Annotations.Labels[k] = v 99 } 100 } 101 if flags.Changed(flagLabelRemove) { 102 keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll() 103 for _, k := range keys { 104 // if a key doesn't exist, fail the command explicitly 105 if _, exists := spec.Annotations.Labels[k]; !exists { 106 return errors.Errorf("key %s doesn't exist in node's labels", k) 107 } 108 delete(spec.Annotations.Labels, k) 109 } 110 } 111 return nil 112 } 113 } 114 115 const ( 116 flagRole = "role" 117 flagAvailability = "availability" 118 flagLabelAdd = "label-add" 119 flagLabelRemove = "label-rm" 120 )