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