Change docker service update semantics

Change `docker service update` to replace attributes of the target
service rather than augment them. One particular occurrence where the
previous behavior proved problematic is when trying to update a port
mapping: the merge semantics provided no way of removing published
ports, but strictly of adding more.

The utility merge* functions where renamed accordingly to update*.

Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
Upstream-commit: 1f8ab93b4422a88ef3053105c17acf1933576fbe
Component: engine
This commit is contained in:
Arnaud Porterie (icecrime)
2016-06-17 16:24:07 -07:00
parent 531b88d2e3
commit 89ce4731a6
3 changed files with 134 additions and 47 deletions

View File

@ -46,7 +46,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri
return err
}
err = mergeService(&service.Spec, flags)
err = updateService(&service.Spec, flags)
if err != nil {
return err
}
@ -59,52 +59,52 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri
return nil
}
func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
func updateService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
mergeString := func(flag string, field *string) {
updateString := func(flag string, field *string) {
if flags.Changed(flag) {
*field, _ = flags.GetString(flag)
}
}
mergeListOpts := func(flag string, field *[]string) {
updateListOpts := func(flag string, field *[]string) {
if flags.Changed(flag) {
value := flags.Lookup(flag).Value.(*opts.ListOpts)
*field = value.GetAll()
}
}
mergeSlice := func(flag string, field *[]string) {
updateSlice := func(flag string, field *[]string) {
if flags.Changed(flag) {
*field, _ = flags.GetStringSlice(flag)
}
}
mergeInt64Value := func(flag string, field *int64) {
updateInt64Value := func(flag string, field *int64) {
if flags.Changed(flag) {
*field = flags.Lookup(flag).Value.(int64Value).Value()
}
}
mergeDuration := func(flag string, field *time.Duration) {
updateDuration := func(flag string, field *time.Duration) {
if flags.Changed(flag) {
*field, _ = flags.GetDuration(flag)
}
}
mergeDurationOpt := func(flag string, field *time.Duration) {
updateDurationOpt := func(flag string, field *time.Duration) {
if flags.Changed(flag) {
*field = *flags.Lookup(flag).Value.(*DurationOpt).Value()
}
}
mergeUint64 := func(flag string, field *uint64) {
updateUint64 := func(flag string, field *uint64) {
if flags.Changed(flag) {
*field, _ = flags.GetUint64(flag)
}
}
mergeUint64Opt := func(flag string, field *uint64) {
updateUint64Opt := func(flag string, field *uint64) {
if flags.Changed(flag) {
*field = *flags.Lookup(flag).Value.(*Uint64Opt).Value()
}
@ -112,23 +112,23 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
cspec := &spec.TaskTemplate.ContainerSpec
task := &spec.TaskTemplate
mergeString(flagName, &spec.Name)
mergeLabels(flags, &spec.Labels)
mergeString("image", &cspec.Image)
mergeSlice("command", &cspec.Command)
mergeSlice("arg", &cspec.Command)
mergeListOpts("env", &cspec.Env)
mergeString("workdir", &cspec.Dir)
mergeString("user", &cspec.User)
mergeMounts(flags, &cspec.Mounts)
updateString(flagName, &spec.Name)
updateLabels(flags, &spec.Labels)
updateString("image", &cspec.Image)
updateSlice("command", &cspec.Command)
updateSlice("arg", &cspec.Command)
updateListOpts("env", &cspec.Env)
updateString("workdir", &cspec.Dir)
updateString("user", &cspec.User)
updateMounts(flags, &cspec.Mounts)
if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
if task.Resources == nil {
task.Resources = &swarm.ResourceRequirements{}
}
task.Resources.Limits = &swarm.Resources{}
mergeInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
mergeInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
updateInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
updateInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
}
if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) {
@ -136,11 +136,11 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
task.Resources = &swarm.ResourceRequirements{}
}
task.Resources.Reservations = &swarm.Resources{}
mergeInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
mergeInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
updateInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
}
mergeDurationOpt("stop-grace-period", cspec.StopGracePeriod)
updateDurationOpt("stop-grace-period", cspec.StopGracePeriod)
if flags.Changed(flagRestartCondition) || flags.Changed(flagRestartDelay) || flags.Changed(flagRestartMaxAttempts) || flags.Changed(flagRestartWindow) {
if task.RestartPolicy == nil {
@ -151,17 +151,17 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
value, _ := flags.GetString(flagRestartCondition)
task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
}
mergeDurationOpt(flagRestartDelay, task.RestartPolicy.Delay)
mergeUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts)
mergeDurationOpt((flagRestartWindow), task.RestartPolicy.Window)
updateDurationOpt(flagRestartDelay, task.RestartPolicy.Delay)
updateUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts)
updateDurationOpt((flagRestartWindow), task.RestartPolicy.Window)
}
if flags.Changed(flagConstraint) {
task.Placement = &swarm.Placement{}
mergeSlice(flagConstraint, &task.Placement.Constraints)
updateSlice(flagConstraint, &task.Placement.Constraints)
}
if err := mergeMode(flags, &spec.Mode); err != nil {
if err := updateMode(flags, &spec.Mode); err != nil {
return err
}
@ -169,11 +169,11 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
if spec.UpdateConfig == nil {
spec.UpdateConfig = &swarm.UpdateConfig{}
}
mergeUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
mergeDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
}
mergeNetworks(flags, &spec.Networks)
updateNetworks(flags, &spec.Networks)
if flags.Changed(flagEndpointMode) {
value, _ := flags.GetString(flagEndpointMode)
spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
@ -183,38 +183,36 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
if spec.EndpointSpec == nil {
spec.EndpointSpec = &swarm.EndpointSpec{}
}
mergePorts(flags, &spec.EndpointSpec.Ports)
updatePorts(flags, &spec.EndpointSpec.Ports)
}
return nil
}
func mergeLabels(flags *pflag.FlagSet, field *map[string]string) {
func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
if !flags.Changed(flagLabel) {
return
}
if *field == nil {
*field = make(map[string]string)
}
values := flags.Lookup(flagLabel).Value.(*opts.ListOpts).GetAll()
localLabels := map[string]string{}
for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
(*field)[key] = value
localLabels[key] = value
}
*field = localLabels
}
// TODO: should this override by destination path, or does swarm handle that?
func mergeMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
func updateMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
if !flags.Changed(flagMount) {
return
}
values := flags.Lookup(flagMount).Value.(*MountOpt).Value()
*mounts = append(*mounts, values...)
*mounts = flags.Lookup(flagMount).Value.(*MountOpt).Value()
}
// TODO: should this override by name, or does swarm handle that?
func mergePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
if !flags.Changed(flagPublish) {
return
}
@ -222,22 +220,27 @@ func mergePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
values := flags.Lookup(flagPublish).Value.(*opts.ListOpts).GetAll()
ports, portBindings, _ := nat.ParsePortSpecs(values)
var localPortConfig []swarm.PortConfig
for port := range ports {
*portConfig = append(*portConfig, convertPortToPortConfig(port, portBindings)...)
localPortConfig = append(localPortConfig, convertPortToPortConfig(port, portBindings)...)
}
*portConfig = localPortConfig
}
func mergeNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) {
func updateNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) {
if !flags.Changed(flagNetwork) {
return
}
networks, _ := flags.GetStringSlice(flagNetwork)
var localAttachments []swarm.NetworkAttachmentConfig
for _, network := range networks {
*attachments = append(*attachments, swarm.NetworkAttachmentConfig{Target: network})
localAttachments = append(localAttachments, swarm.NetworkAttachmentConfig{Target: network})
}
*attachments = localAttachments
}
func mergeMode(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
func updateMode(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
if !flags.Changed(flagMode) && !flags.Changed(flagReplicas) {
return nil
}