github.com/kobeld/docker@v1.12.0-rc1/api/client/service/update.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  	"github.com/docker/docker/api/client"
    10  	"github.com/docker/docker/cli"
    11  	"github.com/docker/docker/opts"
    12  	runconfigopts "github.com/docker/docker/runconfig/opts"
    13  	"github.com/docker/engine-api/types/swarm"
    14  	"github.com/docker/go-connections/nat"
    15  	"github.com/spf13/cobra"
    16  	"github.com/spf13/pflag"
    17  )
    18  
    19  func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
    20  	opts := newServiceOptions()
    21  	var flags *pflag.FlagSet
    22  
    23  	cmd := &cobra.Command{
    24  		Use:   "update [OPTIONS] SERVICE",
    25  		Short: "Update a service",
    26  		Args:  cli.ExactArgs(1),
    27  		RunE: func(cmd *cobra.Command, args []string) error {
    28  			return runUpdate(dockerCli, flags, args[0])
    29  		},
    30  	}
    31  
    32  	flags = cmd.Flags()
    33  	flags.String("image", "", "Service image tag")
    34  	flags.StringSlice("command", []string{}, "Service command")
    35  	flags.StringSlice("arg", []string{}, "Service command args")
    36  	addServiceFlags(cmd, opts)
    37  	return cmd
    38  }
    39  
    40  func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID string) error {
    41  	client := dockerCli.Client()
    42  	ctx := context.Background()
    43  
    44  	service, err := client.ServiceInspect(ctx, serviceID)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	err = mergeService(&service.Spec, flags)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
    59  	return nil
    60  }
    61  
    62  func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
    63  
    64  	mergeString := func(flag string, field *string) {
    65  		if flags.Changed(flag) {
    66  			*field, _ = flags.GetString(flag)
    67  		}
    68  	}
    69  
    70  	mergeListOpts := func(flag string, field *[]string) {
    71  		if flags.Changed(flag) {
    72  			value := flags.Lookup(flag).Value.(*opts.ListOpts)
    73  			*field = value.GetAll()
    74  		}
    75  	}
    76  
    77  	mergeSlice := func(flag string, field *[]string) {
    78  		if flags.Changed(flag) {
    79  			*field, _ = flags.GetStringSlice(flag)
    80  		}
    81  	}
    82  
    83  	mergeInt64Value := func(flag string, field *int64) {
    84  		if flags.Changed(flag) {
    85  			*field = flags.Lookup(flag).Value.(int64Value).Value()
    86  		}
    87  	}
    88  
    89  	mergeDuration := func(flag string, field *time.Duration) {
    90  		if flags.Changed(flag) {
    91  			*field, _ = flags.GetDuration(flag)
    92  		}
    93  	}
    94  
    95  	mergeDurationOpt := func(flag string, field *time.Duration) {
    96  		if flags.Changed(flag) {
    97  			*field = *flags.Lookup(flag).Value.(*DurationOpt).Value()
    98  		}
    99  	}
   100  
   101  	mergeUint64 := func(flag string, field *uint64) {
   102  		if flags.Changed(flag) {
   103  			*field, _ = flags.GetUint64(flag)
   104  		}
   105  	}
   106  
   107  	mergeUint64Opt := func(flag string, field *uint64) {
   108  		if flags.Changed(flag) {
   109  			*field = *flags.Lookup(flag).Value.(*Uint64Opt).Value()
   110  		}
   111  	}
   112  
   113  	cspec := &spec.TaskTemplate.ContainerSpec
   114  	task := &spec.TaskTemplate
   115  	mergeString(flagName, &spec.Name)
   116  	mergeLabels(flags, &spec.Labels)
   117  	mergeString("image", &cspec.Image)
   118  	mergeSlice("command", &cspec.Command)
   119  	mergeSlice("arg", &cspec.Command)
   120  	mergeListOpts("env", &cspec.Env)
   121  	mergeString("workdir", &cspec.Dir)
   122  	mergeString("user", &cspec.User)
   123  	mergeMounts(flags, &cspec.Mounts)
   124  
   125  	if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
   126  		if task.Resources == nil {
   127  			task.Resources = &swarm.ResourceRequirements{}
   128  		}
   129  		task.Resources.Limits = &swarm.Resources{}
   130  		mergeInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
   131  		mergeInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
   132  
   133  	}
   134  	if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) {
   135  		if task.Resources == nil {
   136  			task.Resources = &swarm.ResourceRequirements{}
   137  		}
   138  		task.Resources.Reservations = &swarm.Resources{}
   139  		mergeInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
   140  		mergeInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
   141  	}
   142  
   143  	mergeDurationOpt("stop-grace-period", cspec.StopGracePeriod)
   144  
   145  	if flags.Changed(flagRestartCondition) || flags.Changed(flagRestartDelay) || flags.Changed(flagRestartMaxAttempts) || flags.Changed(flagRestartWindow) {
   146  		if task.RestartPolicy == nil {
   147  			task.RestartPolicy = &swarm.RestartPolicy{}
   148  		}
   149  
   150  		if flags.Changed(flagRestartCondition) {
   151  			value, _ := flags.GetString(flagRestartCondition)
   152  			task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
   153  		}
   154  		mergeDurationOpt(flagRestartDelay, task.RestartPolicy.Delay)
   155  		mergeUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts)
   156  		mergeDurationOpt((flagRestartWindow), task.RestartPolicy.Window)
   157  	}
   158  
   159  	if flags.Changed(flagConstraint) {
   160  		task.Placement = &swarm.Placement{}
   161  		mergeSlice(flagConstraint, &task.Placement.Constraints)
   162  	}
   163  
   164  	if err := mergeMode(flags, &spec.Mode); err != nil {
   165  		return err
   166  	}
   167  
   168  	if flags.Changed(flagUpdateParallelism) || flags.Changed(flagUpdateDelay) {
   169  		if spec.UpdateConfig == nil {
   170  			spec.UpdateConfig = &swarm.UpdateConfig{}
   171  		}
   172  		mergeUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
   173  		mergeDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
   174  	}
   175  
   176  	mergeNetworks(flags, &spec.Networks)
   177  	if flags.Changed(flagEndpointMode) {
   178  		value, _ := flags.GetString(flagEndpointMode)
   179  		spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
   180  	}
   181  
   182  	if flags.Changed(flagPublish) {
   183  		if spec.EndpointSpec == nil {
   184  			spec.EndpointSpec = &swarm.EndpointSpec{}
   185  		}
   186  		mergePorts(flags, &spec.EndpointSpec.Ports)
   187  	}
   188  	return nil
   189  }
   190  
   191  func mergeLabels(flags *pflag.FlagSet, field *map[string]string) {
   192  	if !flags.Changed(flagLabel) {
   193  		return
   194  	}
   195  
   196  	if *field == nil {
   197  		*field = make(map[string]string)
   198  	}
   199  
   200  	values := flags.Lookup(flagLabel).Value.(*opts.ListOpts).GetAll()
   201  	for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
   202  		(*field)[key] = value
   203  	}
   204  }
   205  
   206  // TODO: should this override by destination path, or does swarm handle that?
   207  func mergeMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
   208  	if !flags.Changed(flagMount) {
   209  		return
   210  	}
   211  
   212  	values := flags.Lookup(flagMount).Value.(*MountOpt).Value()
   213  	*mounts = append(*mounts, values...)
   214  }
   215  
   216  // TODO: should this override by name, or does swarm handle that?
   217  func mergePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
   218  	if !flags.Changed(flagPublish) {
   219  		return
   220  	}
   221  
   222  	values := flags.Lookup(flagPublish).Value.(*opts.ListOpts).GetAll()
   223  	ports, portBindings, _ := nat.ParsePortSpecs(values)
   224  
   225  	for port := range ports {
   226  		*portConfig = append(*portConfig, convertPortToPortConfig(port, portBindings)...)
   227  	}
   228  }
   229  
   230  func mergeNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) {
   231  	if !flags.Changed(flagNetwork) {
   232  		return
   233  	}
   234  	networks, _ := flags.GetStringSlice(flagNetwork)
   235  	for _, network := range networks {
   236  		*attachments = append(*attachments, swarm.NetworkAttachmentConfig{Target: network})
   237  	}
   238  }
   239  
   240  func mergeMode(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
   241  	if !flags.Changed(flagMode) && !flags.Changed(flagReplicas) {
   242  		return nil
   243  	}
   244  
   245  	var mode string
   246  	if flags.Changed(flagMode) {
   247  		mode, _ = flags.GetString(flagMode)
   248  	}
   249  
   250  	if !(mode == "replicated" || serviceMode.Replicated != nil) && flags.Changed(flagReplicas) {
   251  		return fmt.Errorf("replicas can only be used with replicated mode")
   252  	}
   253  
   254  	if mode == "global" {
   255  		serviceMode.Replicated = nil
   256  		serviceMode.Global = &swarm.GlobalService{}
   257  		return nil
   258  	}
   259  
   260  	if flags.Changed(flagReplicas) {
   261  		replicas := flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value()
   262  		serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas}
   263  		serviceMode.Global = nil
   264  		return nil
   265  	}
   266  
   267  	if mode == "replicated" {
   268  		if serviceMode.Replicated != nil {
   269  			return nil
   270  		}
   271  		serviceMode.Replicated = &swarm.ReplicatedService{Replicas: &DefaultReplicas}
   272  		serviceMode.Global = nil
   273  	}
   274  
   275  	return nil
   276  }