summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastiaan van Stijn <thaJeztah@users.noreply.github.com>2017-04-11 13:06:53 +0200
committerGitHub <noreply@github.com>2017-04-11 13:06:53 +0200
commita258ef58d8a100467d5d948b026a884ebe58eaf4 (patch)
tree849063d499bf7ddd03304c77f1473470f485aafa
parent5e9a0efec714b46af3de51a22bdd344a600f84eb (diff)
parentbbe1202410a580b8bdb35e7b50e4e4028e530111 (diff)
downloaddocker-a258ef58d8a100467d5d948b026a884ebe58eaf4.tar.gz
Merge pull request #32284 from aaronlehmann/fix-service-defaults
Improve default handling for "service create"
-rw-r--r--api/server/router/swarm/backend.go6
-rw-r--r--api/server/router/swarm/cluster_routes.go12
-rw-r--r--api/server/router/swarm/helpers.go2
-rw-r--r--api/swagger.yaml5
-rw-r--r--api/types/client.go10
-rw-r--r--cli/command/idresolver/client_test.go3
-rw-r--r--cli/command/idresolver/idresolver.go3
-rw-r--r--cli/command/service/create.go4
-rw-r--r--cli/command/service/inspect.go4
-rw-r--r--cli/command/service/logs.go2
-rw-r--r--cli/command/service/opts.go263
-rw-r--r--cli/command/service/progress/progress.go2
-rw-r--r--cli/command/service/scale.go2
-rw-r--r--cli/command/service/update.go12
-rw-r--r--cli/command/system/inspect.go4
-rw-r--r--client/interface.go2
-rw-r--r--client/service_inspect.go9
-rw-r--r--client/service_inspect_test.go7
-rw-r--r--daemon/cluster/helpers.go14
-rw-r--r--daemon/cluster/services.go10
-rw-r--r--daemon/cluster/tasks.go2
-rw-r--r--docs/reference/commandline/service_create.md55
-rw-r--r--docs/reference/commandline/service_update.md77
-rw-r--r--integration-cli/docker_api_swarm_service_test.go10
-rw-r--r--opts/opts.go8
-rw-r--r--opts/opts_test.go12
26 files changed, 392 insertions, 148 deletions
diff --git a/api/server/router/swarm/backend.go b/api/server/router/swarm/backend.go
index 28b9a98018..3a5da97d2c 100644
--- a/api/server/router/swarm/backend.go
+++ b/api/server/router/swarm/backend.go
@@ -17,7 +17,7 @@ type Backend interface {
GetUnlockKey() (string, error)
UnlockSwarm(req types.UnlockRequest) error
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
- GetService(string) (types.Service, error)
+ GetService(idOrName string, insertDefaults bool) (types.Service, error)
CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
UpdateService(string, uint64, types.ServiceSpec, basictypes.ServiceUpdateOptions) (*basictypes.ServiceUpdateResponse, error)
RemoveService(string) error
@@ -30,7 +30,7 @@ type Backend interface {
GetTask(string) (types.Task, error)
GetSecrets(opts basictypes.SecretListOptions) ([]types.Secret, error)
CreateSecret(s types.SecretSpec) (string, error)
- RemoveSecret(id string) error
+ RemoveSecret(idOrName string) error
GetSecret(id string) (types.Secret, error)
- UpdateSecret(id string, version uint64, spec types.SecretSpec) error
+ UpdateSecret(idOrName string, version uint64, spec types.SecretSpec) error
}
diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go
index dfae13f1dd..4c60b6b6ee 100644
--- a/api/server/router/swarm/cluster_routes.go
+++ b/api/server/router/swarm/cluster_routes.go
@@ -151,7 +151,17 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
}
func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
- service, err := sr.backend.GetService(vars["id"])
+ var insertDefaults bool
+ if value := r.URL.Query().Get("insertDefaults"); value != "" {
+ var err error
+ insertDefaults, err = strconv.ParseBool(value)
+ if err != nil {
+ err := fmt.Errorf("invalid value for insertDefaults: %s", value)
+ return errors.NewBadRequestError(err)
+ }
+ }
+
+ service, err := sr.backend.GetService(vars["id"], insertDefaults)
if err != nil {
logrus.Errorf("Error getting service %s: %v", vars["id"], err)
return err
diff --git a/api/server/router/swarm/helpers.go b/api/server/router/swarm/helpers.go
index af745b84c3..ea692ea368 100644
--- a/api/server/router/swarm/helpers.go
+++ b/api/server/router/swarm/helpers.go
@@ -39,7 +39,7 @@ func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *
// checking for whether logs are TTY involves iterating over every service
// and task. idk if there is a better way
for _, service := range selector.Services {
- s, err := sr.backend.GetService(service)
+ s, err := sr.backend.GetService(service, false)
if err != nil {
// maybe should return some context with this error?
return err
diff --git a/api/swagger.yaml b/api/swagger.yaml
index f6e6fc628f..36927b5a04 100644
--- a/api/swagger.yaml
+++ b/api/swagger.yaml
@@ -7584,6 +7584,11 @@ paths:
description: "ID or name of service."
required: true
type: "string"
+ - name: "insertDefaults"
+ in: "query"
+ description: "Fill empty fields with default values."
+ type: "boolean"
+ default: false
tags: ["Service"]
delete:
summary: "Delete a service"
diff --git a/api/types/client.go b/api/types/client.go
index 2bf6ad01a9..d7bc55011c 100644
--- a/api/types/client.go
+++ b/api/types/client.go
@@ -316,12 +316,18 @@ type ServiceUpdateOptions struct {
Rollback string
}
-// ServiceListOptions holds parameters to list services with.
+// ServiceListOptions holds parameters to list services with.
type ServiceListOptions struct {
Filters filters.Args
}
-// TaskListOptions holds parameters to list tasks with.
+// ServiceInspectOptions holds parameters related to the "service inspect"
+// operation.
+type ServiceInspectOptions struct {
+ InsertDefaults bool
+}
+
+// TaskListOptions holds parameters to list tasks with.
type TaskListOptions struct {
Filters filters.Args
}
diff --git a/cli/command/idresolver/client_test.go b/cli/command/idresolver/client_test.go
index 8c02d7ebcf..f84683b907 100644
--- a/cli/command/idresolver/client_test.go
+++ b/cli/command/idresolver/client_test.go
@@ -1,6 +1,7 @@
package idresolver
import (
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"golang.org/x/net/context"
@@ -19,7 +20,7 @@ func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (s
return swarm.Node{}, []byte{}, nil
}
-func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) {
+func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
if cli.serviceInspectFunc != nil {
return cli.serviceInspectFunc(serviceID)
}
diff --git a/cli/command/idresolver/idresolver.go b/cli/command/idresolver/idresolver.go
index 25c51a27eb..6088b64b59 100644
--- a/cli/command/idresolver/idresolver.go
+++ b/cli/command/idresolver/idresolver.go
@@ -3,6 +3,7 @@ package idresolver
import (
"golang.org/x/net/context"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/pkg/errors"
@@ -39,7 +40,7 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string,
}
return id, nil
case swarm.Service:
- service, _, err := r.client.ServiceInspectWithRaw(ctx, id)
+ service, _, err := r.client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
if err != nil {
return id, nil
}
diff --git a/cli/command/service/create.go b/cli/command/service/create.go
index 0e77f73d32..bb2a1fe3b7 100644
--- a/cli/command/service/create.go
+++ b/cli/command/service/create.go
@@ -30,7 +30,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.StringVar(&opts.mode, flagMode, "replicated", "Service mode (replicated or global)")
flags.StringVar(&opts.name, flagName, "", "Service name")
- addServiceFlags(flags, opts)
+ addServiceFlags(flags, opts, buildServiceDefaultFlagMapping())
flags.VarP(&opts.labels, flagLabel, "l", "Service labels")
flags.Var(&opts.containerLabels, flagContainerLabel, "Container labels")
@@ -65,7 +65,7 @@ func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *service
ctx := context.Background()
- service, err := opts.ToService(ctx, apiClient)
+ service, err := opts.ToService(ctx, apiClient, flags)
if err != nil {
return err
}
diff --git a/cli/command/service/inspect.go b/cli/command/service/inspect.go
index 8a8b51cd0e..fae24eeaf1 100644
--- a/cli/command/service/inspect.go
+++ b/cli/command/service/inspect.go
@@ -5,6 +5,7 @@ import (
"golang.org/x/net/context"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/formatter"
@@ -51,7 +52,8 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
}
getRef := func(ref string) (interface{}, []byte, error) {
- service, _, err := client.ServiceInspectWithRaw(ctx, ref)
+ // Service inspect shows defaults values in empty fields.
+ service, _, err := client.ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
if err == nil || !apiclient.IsErrServiceNotFound(err) {
return service, nil, err
}
diff --git a/cli/command/service/logs.go b/cli/command/service/logs.go
index 30ed504cfa..2440c1680e 100644
--- a/cli/command/service/logs.go
+++ b/cli/command/service/logs.go
@@ -86,7 +86,7 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
tty bool
)
- service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target)
+ service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target, types.ServiceInspectOptions{})
if err != nil {
// if it's any error other than service not found, it's Real
if !client.IsErrServiceNotFound(err) {
diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go
index 066436838c..4211c5bf8c 100644
--- a/cli/command/service/opts.go
+++ b/cli/command/service/opts.go
@@ -12,7 +12,10 @@ import (
"github.com/docker/docker/client"
"github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts"
+ "github.com/docker/swarmkit/api"
+ "github.com/docker/swarmkit/api/defaults"
shlex "github.com/flynn-archive/go-shlex"
+ gogotypes "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"golang.org/x/net/context"
@@ -177,6 +180,9 @@ func (s *ShlexOpt) Type() string {
}
func (s *ShlexOpt) String() string {
+ if len(*s) == 0 {
+ return ""
+ }
return fmt.Sprint(*s)
}
@@ -194,15 +200,75 @@ type updateOptions struct {
order string
}
-func (opts updateOptions) config() *swarm.UpdateConfig {
+func updateConfigFromDefaults(defaultUpdateConfig *api.UpdateConfig) *swarm.UpdateConfig {
+ defaultFailureAction := strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaultUpdateConfig.FailureAction)])
+ defaultMonitor, _ := gogotypes.DurationFromProto(defaultUpdateConfig.Monitor)
return &swarm.UpdateConfig{
- Parallelism: opts.parallelism,
- Delay: opts.delay,
- Monitor: opts.monitor,
- FailureAction: opts.onFailure,
- MaxFailureRatio: opts.maxFailureRatio.Value(),
- Order: opts.order,
+ Parallelism: defaultUpdateConfig.Parallelism,
+ Delay: defaultUpdateConfig.Delay,
+ Monitor: defaultMonitor,
+ FailureAction: defaultFailureAction,
+ MaxFailureRatio: defaultUpdateConfig.MaxFailureRatio,
+ Order: defaultOrder(defaultUpdateConfig.Order),
+ }
+}
+
+func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
+ if !anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
+ return nil
+ }
+
+ updateConfig := updateConfigFromDefaults(defaults.Service.Update)
+
+ if flags.Changed(flagUpdateParallelism) {
+ updateConfig.Parallelism = opts.parallelism
+ }
+ if flags.Changed(flagUpdateDelay) {
+ updateConfig.Delay = opts.delay
+ }
+ if flags.Changed(flagUpdateMonitor) {
+ updateConfig.Monitor = opts.monitor
+ }
+ if flags.Changed(flagUpdateFailureAction) {
+ updateConfig.FailureAction = opts.onFailure
+ }
+ if flags.Changed(flagUpdateMaxFailureRatio) {
+ updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
+ }
+ if flags.Changed(flagUpdateOrder) {
+ updateConfig.Order = opts.order
+ }
+
+ return updateConfig
+}
+
+func (opts updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
+ if !anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio) {
+ return nil
+ }
+
+ updateConfig := updateConfigFromDefaults(defaults.Service.Rollback)
+
+ if flags.Changed(flagRollbackParallelism) {
+ updateConfig.Parallelism = opts.parallelism
+ }
+ if flags.Changed(flagRollbackDelay) {
+ updateConfig.Delay = opts.delay
+ }
+ if flags.Changed(flagRollbackMonitor) {
+ updateConfig.Monitor = opts.monitor
+ }
+ if flags.Changed(flagRollbackFailureAction) {
+ updateConfig.FailureAction = opts.onFailure
}
+ if flags.Changed(flagRollbackMaxFailureRatio) {
+ updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
+ }
+ if flags.Changed(flagRollbackOrder) {
+ updateConfig.Order = opts.order
+ }
+
+ return updateConfig
}
type resourceOptions struct {
@@ -232,13 +298,70 @@ type restartPolicyOptions struct {
window DurationOpt
}
-func (r *restartPolicyOptions) ToRestartPolicy() *swarm.RestartPolicy {
- return &swarm.RestartPolicy{
- Condition: swarm.RestartPolicyCondition(r.condition),
- Delay: r.delay.Value(),
- MaxAttempts: r.maxAttempts.Value(),
- Window: r.window.Value(),
+func defaultRestartPolicy() *swarm.RestartPolicy {
+ defaultMaxAttempts := defaults.Service.Task.Restart.MaxAttempts
+ rp := &swarm.RestartPolicy{
+ MaxAttempts: &defaultMaxAttempts,
+ }
+
+ if defaults.Service.Task.Restart.Delay != nil {
+ defaultRestartDelay, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
+ rp.Delay = &defaultRestartDelay
+ }
+ if defaults.Service.Task.Restart.Window != nil {
+ defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
+ rp.Window = &defaultRestartWindow
+ }
+ rp.Condition = defaultRestartCondition()
+
+ return rp
+}
+
+func defaultRestartCondition() swarm.RestartPolicyCondition {
+ switch defaults.Service.Task.Restart.Condition {
+ case api.RestartOnNone:
+ return "none"
+ case api.RestartOnFailure:
+ return "on-failure"
+ case api.RestartOnAny:
+ return "any"
+ default:
+ return ""
+ }
+}
+
+func defaultOrder(order api.UpdateConfig_UpdateOrder) string {
+ switch order {
+ case api.UpdateConfig_STOP_FIRST:
+ return "stop-first"
+ case api.UpdateConfig_START_FIRST:
+ return "start-first"
+ default:
+ return ""
+ }
+}
+
+func (r *restartPolicyOptions) ToRestartPolicy(flags *pflag.FlagSet) *swarm.RestartPolicy {
+ if !anyChanged(flags, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow, flagRestartCondition) {
+ return nil
+ }
+
+ restartPolicy := defaultRestartPolicy()
+
+ if flags.Changed(flagRestartDelay) {
+ restartPolicy.Delay = r.delay.Value()
+ }
+ if flags.Changed(flagRestartCondition) {
+ restartPolicy.Condition = swarm.RestartPolicyCondition(r.condition)
}
+ if flags.Changed(flagRestartMaxAttempts) {
+ restartPolicy.MaxAttempts = r.maxAttempts.Value()
+ }
+ if flags.Changed(flagRestartWindow) {
+ restartPolicy.Window = r.window.Value()
+ }
+
+ return restartPolicy
}
type credentialSpecOpt struct {
@@ -463,7 +586,14 @@ func (opts *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
return serviceMode, nil
}
-func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIClient) (swarm.ServiceSpec, error) {
+func (opts *serviceOptions) ToStopGracePeriod(flags *pflag.FlagSet) *time.Duration {
+ if flags.Changed(flagStopGracePeriod) {
+ return opts.stopGrace.Value()
+ }
+ return nil
+}
+
+func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIClient, flags *pflag.FlagSet) (swarm.ServiceSpec, error) {
var service swarm.ServiceSpec
envVariables, err := runconfigopts.ReadKVStrings(opts.envFile.GetAll(), opts.env.GetAll())
@@ -526,13 +656,13 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
Options: opts.dnsOption.GetAll(),
},
Hosts: convertExtraHostsToSwarmHosts(opts.hosts.GetAll()),
- StopGracePeriod: opts.stopGrace.Value(),
+ StopGracePeriod: opts.ToStopGracePeriod(flags),
Secrets: nil,
Healthcheck: healthConfig,
},
Networks: networks,
Resources: opts.resources.ToResourceRequirements(),
- RestartPolicy: opts.restartPolicy.ToRestartPolicy(),
+ RestartPolicy: opts.restartPolicy.ToRestartPolicy(flags),
Placement: &swarm.Placement{
Constraints: opts.constraints.GetAll(),
Preferences: opts.placementPrefs.prefs,
@@ -540,8 +670,8 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
LogDriver: opts.logDriver.toLogDriver(),
},
Mode: serviceMode,
- UpdateConfig: opts.update.config(),
- RollbackConfig: opts.rollback.config(),
+ UpdateConfig: opts.update.updateConfig(flags),
+ RollbackConfig: opts.update.rollbackConfig(flags),
EndpointSpec: opts.endpoint.ToEndpointSpec(),
}
@@ -554,9 +684,67 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
return service, nil
}
+type flagDefaults map[string]interface{}
+
+func (fd flagDefaults) getUint64(flagName string) uint64 {
+ if val, ok := fd[flagName].(uint64); ok {
+ return val
+ }
+ return 0
+}
+
+func (fd flagDefaults) getString(flagName string) string {
+ if val, ok := fd[flagName].(string); ok {
+ return val
+ }
+ return ""
+}
+
+func buildServiceDefaultFlagMapping() flagDefaults {
+ defaultFlagValues := make(map[string]interface{})
+
+ defaultFlagValues[flagStopGracePeriod], _ = gogotypes.DurationFromProto(defaults.Service.Task.GetContainer().StopGracePeriod)
+ defaultFlagValues[flagRestartCondition] = `"` + defaultRestartCondition() + `"`
+ defaultFlagValues[flagRestartDelay], _ = gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
+
+ if defaults.Service.Task.Restart.MaxAttempts != 0 {
+ defaultFlagValues[flagRestartMaxAttempts] = defaults.Service.Task.Restart.MaxAttempts
+ }
+
+ defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
+ if defaultRestartWindow != 0 {
+ defaultFlagValues[flagRestartWindow] = defaultRestartWindow
+ }
+
+ defaultFlagValues[flagUpdateParallelism] = defaults.Service.Update.Parallelism
+ defaultFlagValues[flagUpdateDelay] = defaults.Service.Update.Delay
+ defaultFlagValues[flagUpdateMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Update.Monitor)
+ defaultFlagValues[flagUpdateFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Update.FailureAction)]) + `"`
+ defaultFlagValues[flagUpdateMaxFailureRatio] = defaults.Service.Update.MaxFailureRatio
+ defaultFlagValues[flagUpdateOrder] = `"` + defaultOrder(defaults.Service.Update.Order) + `"`
+
+ defaultFlagValues[flagRollbackParallelism] = defaults.Service.Rollback.Parallelism
+ defaultFlagValues[flagRollbackDelay] = defaults.Service.Rollback.Delay
+ defaultFlagValues[flagRollbackMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Rollback.Monitor)
+ defaultFlagValues[flagRollbackFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Rollback.FailureAction)]) + `"`
+ defaultFlagValues[flagRollbackMaxFailureRatio] = defaults.Service.Rollback.MaxFailureRatio
+ defaultFlagValues[flagRollbackOrder] = `"` + defaultOrder(defaults.Service.Rollback.Order) + `"`
+
+ defaultFlagValues[flagEndpointMode] = "vip"
+
+ return defaultFlagValues
+}
+
// addServiceFlags adds all flags that are common to both `create` and `update`.
// Any flags that are not common are added separately in the individual command
-func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
+func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValues flagDefaults) {
+ flagDesc := func(flagName string, desc string) string {
+ if defaultValue, ok := defaultFlagValues[flagName]; ok {
+ return fmt.Sprintf("%s (default %v)", desc, defaultValue)
+ }
+ return desc
+ }
+
flags.BoolVarP(&opts.detach, "detach", "d", true, "Exit immediately instead of waiting for the service to converge")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress progress output")
@@ -572,39 +760,40 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
flags.Var(&opts.resources.resCPU, flagReserveCPU, "Reserve CPUs")
flags.Var(&opts.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
- flags.Var(&opts.stopGrace, flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)")
+ flags.Var(&opts.stopGrace, flagStopGracePeriod, flagDesc(flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)"))
flags.Var(&opts.replicas, flagReplicas, "Number of tasks")
- flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", `Restart when condition is met ("none"|"on-failure"|"any")`)
- flags.Var(&opts.restartPolicy.delay, flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)")
- flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, "Maximum number of restarts before giving up")
- flags.Var(&opts.restartPolicy.window, flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)")
+ flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", flagDesc(flagRestartCondition, `Restart when condition is met ("none"|"on-failure"|"any")`))
+ flags.Var(&opts.restartPolicy.delay, flagRestartDelay, flagDesc(flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)"))
+ flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, flagDesc(flagRestartMaxAttempts, "Maximum number of restarts before giving up"))
+
+ flags.Var(&opts.restartPolicy.window, flagRestartWindow, flagDesc(flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)"))
- flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, 1, "Maximum number of tasks updated simultaneously (0 to update all at once)")
- flags.DurationVar(&opts.update.delay, flagUpdateDelay, time.Duration(0), "Delay between updates (ns|us|ms|s|m|h) (default 0s)")
- flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, time.Duration(0), "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)")
+ flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, defaultFlagValues.getUint64(flagUpdateParallelism), "Maximum number of tasks updated simultaneously (0 to update all at once)")
+ flags.DurationVar(&opts.update.delay, flagUpdateDelay, 0, flagDesc(flagUpdateDelay, "Delay between updates (ns|us|ms|s|m|h)"))
+ flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, 0, flagDesc(flagUpdateMonitor, "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)"))
flags.SetAnnotation(flagUpdateMonitor, "version", []string{"1.25"})
- flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "pause", `Action on update failure ("pause"|"continue"|"rollback")`)
- flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update")
+ flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "", flagDesc(flagUpdateFailureAction, `Action on update failure ("pause"|"continue"|"rollback")`))
+ flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, flagDesc(flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update"))
flags.SetAnnotation(flagUpdateMaxFailureRatio, "version", []string{"1.25"})
- flags.StringVar(&opts.update.order, flagUpdateOrder, "stop-first", `Update order ("start-first"|"stop-first")`)
+ flags.StringVar(&opts.update.order, flagUpdateOrder, "", flagDesc(flagUpdateOrder, `Update order ("start-first"|"stop-first")`))
flags.SetAnnotation(flagUpdateOrder, "version", []string{"1.29"})
- flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, 1, "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
+ flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, defaultFlagValues.getUint64(flagRollbackParallelism), "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
flags.SetAnnotation(flagRollbackParallelism, "version", []string{"1.28"})
- flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, time.Duration(0), "Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)")
+ flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, 0, flagDesc(flagRollbackDelay, "Delay between task rollbacks (ns|us|ms|s|m|h)"))
flags.SetAnnotation(flagRollbackDelay, "version", []string{"1.28"})
- flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, time.Duration(0), "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h) (default 0s)")
+ flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, 0, flagDesc(flagRollbackMonitor, "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)"))
flags.SetAnnotation(flagRollbackMonitor, "version", []string{"1.28"})
- flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "pause", `Action on rollback failure ("pause"|"continue")`)
+ flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "", flagDesc(flagRollbackFailureAction, `Action on rollback failure ("pause"|"continue")`))
flags.SetAnnotation(flagRollbackFailureAction, "version", []string{"1.28"})
- flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback")
+ flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, flagDesc(flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback"))
flags.SetAnnotation(flagRollbackMaxFailureRatio, "version", []string{"1.28"})
- flags.StringVar(&opts.rollback.order, flagRollbackOrder, "stop-first", `Rollback order ("start-first"|"stop-first")`)
+ flags.StringVar(&opts.rollback.order, flagRollbackOrder, "", flagDesc(flagRollbackOrder, `Rollback order ("start-first"|"stop-first")`))
flags.SetAnnotation(flagRollbackOrder, "version", []string{"1.29"})
- flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "vip", "Endpoint mode (vip or dnsrr)")
+ flags.StringVar(&opts.endpoint.mode, flagEndpointMode, defaultFlagValues.getString(flagEndpointMode), "Endpoint mode (vip or dnsrr)")
flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to swarm agents")
diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go
index ccc7e60cfc..bfeaa314a4 100644
--- a/cli/command/service/progress/progress.go
+++ b/cli/command/service/progress/progress.go
@@ -85,7 +85,7 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
)
for {
- service, _, err := client.ServiceInspectWithRaw(ctx, serviceID)
+ service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
if err != nil {
return err
}
diff --git a/cli/command/service/scale.go b/cli/command/service/scale.go
index ed76c862fe..98163c87c9 100644
--- a/cli/command/service/scale.go
+++ b/cli/command/service/scale.go
@@ -71,7 +71,7 @@ func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint6
client := dockerCli.Client()
ctx := context.Background()
- service, _, err := client.ServiceInspectWithRaw(ctx, serviceID)
+ service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
if err != nil {
return err
}
diff --git a/cli/command/service/update.go b/cli/command/service/update.go
index b59f163829..233da68eee 100644
--- a/cli/command/service/update.go
+++ b/cli/command/service/update.go
@@ -17,6 +17,7 @@ import (
"github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/go-connections/nat"
+ "github.com/docker/swarmkit/api/defaults"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -42,7 +43,7 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.SetAnnotation("rollback", "version", []string{"1.25"})
flags.Bool("force", false, "Force update even if no changes require it")
flags.SetAnnotation("force", "version", []string{"1.25"})
- addServiceFlags(flags, serviceOpts)
+ addServiceFlags(flags, serviceOpts, nil)
flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
flags.Var(newListOptsVar(), flagGroupRemove, "Remove a previously added supplementary user group from the container")
@@ -101,7 +102,7 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *service
apiClient := dockerCli.Client()
ctx := context.Background()
- service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID)
+ service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
if err != nil {
return err
}
@@ -294,9 +295,8 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
if task.RestartPolicy == nil {
- task.RestartPolicy = &swarm.RestartPolicy{}
+ task.RestartPolicy = defaultRestartPolicy()
}
-
if flags.Changed(flagRestartCondition) {
value, _ := flags.GetString(flagRestartCondition)
task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
@@ -332,7 +332,7 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio, flagUpdateOrder) {
if spec.UpdateConfig == nil {
- spec.UpdateConfig = &swarm.UpdateConfig{}
+ spec.UpdateConfig = updateConfigFromDefaults(defaults.Service.Update)
}
updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
@@ -344,7 +344,7 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
if anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio, flagRollbackOrder) {
if spec.RollbackConfig == nil {
- spec.RollbackConfig = &swarm.UpdateConfig{}
+ spec.RollbackConfig = updateConfigFromDefaults(defaults.Service.Rollback)
}
updateUint64(flagRollbackParallelism, &spec.RollbackConfig.Parallelism)
updateDuration(flagRollbackDelay, &spec.RollbackConfig.Delay)
diff --git a/cli/command/system/inspect.go b/cli/command/system/inspect.go
index 361902a9e7..ad23d35a09 100644
--- a/cli/command/system/inspect.go
+++ b/cli/command/system/inspect.go
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/inspect"
@@ -79,7 +80,8 @@ func inspectNode(ctx context.Context, dockerCli *command.DockerCli) inspect.GetR
func inspectService(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
return func(ref string) (interface{}, []byte, error) {
- return dockerCli.Client().ServiceInspectWithRaw(ctx, ref)
+ // Service inspect shows defaults values in empty fields.
+ return dockerCli.Client().ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
}
}
diff --git a/client/interface.go b/client/interface.go
index 6f8c094b31..8dbe4300dc 100644
--- a/client/interface.go
+++ b/client/interface.go
@@ -123,7 +123,7 @@ type PluginAPIClient interface {
// ServiceAPIClient defines API client methods for the services
type ServiceAPIClient interface {
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error)
- ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
+ ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
ServiceRemove(ctx context.Context, serviceID string) error
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
diff --git a/client/service_inspect.go b/client/service_inspect.go
index ca71cbde1a..d7e051e3a4 100644
--- a/client/service_inspect.go
+++ b/client/service_inspect.go
@@ -3,16 +3,21 @@ package client
import (
"bytes"
"encoding/json"
+ "fmt"
"io/ioutil"
"net/http"
+ "net/url"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
// ServiceInspectWithRaw returns the service information and the raw data.
-func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) {
- serverResp, err := cli.get(ctx, "/services/"+serviceID, nil, nil)
+func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) {
+ query := url.Values{}
+ query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
+ serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
if err != nil {
if serverResp.statusCode == http.StatusNotFound {
return swarm.Service{}, nil, serviceNotFoundError{serviceID}
diff --git a/client/service_inspect_test.go b/client/service_inspect_test.go
index 0346847317..d53f583e90 100644
--- a/client/service_inspect_test.go
+++ b/client/service_inspect_test.go
@@ -9,6 +9,7 @@ import (
"strings"
"testing"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
@@ -18,7 +19,7 @@ func TestServiceInspectError(t *testing.T) {
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
}
- _, _, err := client.ServiceInspectWithRaw(context.Background(), "nothing")
+ _, _, err := client.ServiceInspectWithRaw(context.Background(), "nothing", types.ServiceInspectOptions{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
@@ -29,7 +30,7 @@ func TestServiceInspectServiceNotFound(t *testing.T) {
client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
}
- _, _, err := client.ServiceInspectWithRaw(context.Background(), "unknown")
+ _, _, err := client.ServiceInspectWithRaw(context.Background(), "unknown", types.ServiceInspectOptions{})
if err == nil || !IsErrServiceNotFound(err) {
t.Fatalf("expected a serviceNotFoundError error, got %v", err)
}
@@ -55,7 +56,7 @@ func TestServiceInspect(t *testing.T) {
}),
}
- serviceInspect, _, err := client.ServiceInspectWithRaw(context.Background(), "service_id")
+ serviceInspect, _, err := client.ServiceInspectWithRaw(context.Background(), "service_id", types.ServiceInspectOptions{})
if err != nil {
t.Fatal(err)
}
diff --git a/daemon/cluster/helpers.go b/daemon/cluster/helpers.go
index 6523a80e1c..98c7cc5472 100644
--- a/daemon/cluster/helpers.go
+++ b/daemon/cluster/helpers.go
@@ -58,9 +58,9 @@ func getNode(ctx context.Context, c swarmapi.ControlClient, input string) (*swar
return rl.Nodes[0], nil
}
-func getService(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Service, error) {
+func getService(ctx context.Context, c swarmapi.ControlClient, input string, insertDefaults bool) (*swarmapi.Service, error) {
// GetService to match via full ID.
- if rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: input}); err == nil {
+ if rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: input, InsertDefaults: insertDefaults}); err == nil {
return rg.Service, nil
}
@@ -91,7 +91,15 @@ func getService(ctx context.Context, c swarmapi.ControlClient, input string) (*s
return nil, fmt.Errorf("service %s is ambiguous (%d matches found)", input, l)
}
- return rl.Services[0], nil
+ if !insertDefaults {
+ return rl.Services[0], nil
+ }
+
+ rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: rl.Services[0].ID, InsertDefaults: true})
+ if err == nil {
+ return rg.Service, nil
+ }
+ return nil, err
}
func getTask(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Task, error) {
diff --git a/daemon/cluster/services.go b/daemon/cluster/services.go
index 8fd730eee7..8d5d4a5edd 100644
--- a/daemon/cluster/services.go
+++ b/daemon/cluster/services.go
@@ -87,10 +87,10 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
}
// GetService returns a service based on an ID or name.
-func (c *Cluster) GetService(input string) (types.Service, error) {
+func (c *Cluster) GetService(input string, insertDefaults bool) (types.Service, error) {
var service *swarmapi.Service
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
- s, err := getService(ctx, state.controlClient, input)
+ s, err := getService(ctx, state.controlClient, input, insertDefaults)
if err != nil {
return err
}
@@ -187,7 +187,7 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
return apierrors.NewBadRequestError(err)
}
- currentService, err := getService(ctx, state.controlClient, serviceIDOrName)
+ currentService, err := getService(ctx, state.controlClient, serviceIDOrName, false)
if err != nil {
return err
}
@@ -289,7 +289,7 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
// RemoveService removes a service from a managed swarm cluster.
func (c *Cluster) RemoveService(input string) error {
return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
- service, err := getService(ctx, state.controlClient, input)
+ service, err := getService(ctx, state.controlClient, input, false)
if err != nil {
return err
}
@@ -442,7 +442,7 @@ func convertSelector(ctx context.Context, cc swarmapi.ControlClient, selector *b
// don't rely on swarmkit to resolve IDs, do it ourselves
swarmSelector := &swarmapi.LogSelector{}
for _, s := range selector.Services {
- service, err := getService(ctx, cc, s)
+ service, err := getService(ctx, cc, s, false)
if err != nil {
return nil, err
}
diff --git a/daemon/cluster/tasks.go b/daemon/cluster/tasks.go
index 001a345a68..6a6c59ffe5 100644
--- a/daemon/cluster/tasks.go
+++ b/daemon/cluster/tasks.go
@@ -23,7 +23,7 @@ func (c *Cluster) GetTasks(options apitypes.TaskListOptions) ([]types.Task, erro
if filter.Include("service") {
serviceFilters := filter.Get("service")
for _, serviceFilter := range serviceFilters {
- service, err := c.GetService(serviceFilter)
+ service, err := c.GetService(serviceFilter, false)
if err != nil {
return err
}
diff --git a/docs/reference/commandline/service_create.md b/docs/reference/commandline/service_create.md
index 9490f1bb9f..082dffb827 100644
--- a/docs/reference/commandline/service_create.md
+++ b/docs/reference/commandline/service_create.md
@@ -21,61 +21,60 @@ Usage: docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
Create a new service
Options:
- --constraint list Placement constraints (default [])
- --container-label list Container labels (default [])
- -d, --detach Exit immediately instead of waiting for the service to converge
- (default true)
- --dns list Set custom DNS servers (default [])
- --dns-option list Set DNS options (default [])
- --dns-search list Set custom DNS search domains (default [])
- --endpoint-mode string Endpoint mode ("vip"|"dnsrr") (default "vip")
- -e, --env list Set environment variables (default [])
- --env-file list Read in a file of environment variables (default [])
- --group list Set one or more supplementary user groups for the container (default [])
+ --constraint list Placement constraints
+ --container-label list Container labels
+ -d, --detach Exit immediately instead of waiting for the service to converge (default true)
+ --dns list Set custom DNS servers
+ --dns-option list Set DNS options
+ --dns-search list Set custom DNS search domains
+ --endpoint-mode string Endpoint mode (vip or dnsrr) (default "vip")
+ --entrypoint command Overwrite the default ENTRYPOINT of the image
+ -e, --env list Set environment variables
+ --env-file list Read in a file of environment variables
+ --group list Set one or more supplementary user groups for the container
--health-cmd string Command to run to check health
--health-interval duration Time between running the check (ns|us|ms|s|m|h)
--health-retries int Consecutive failures needed to report unhealthy
+ --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)
--health-timeout duration Maximum time to allow one check to run (ns|us|ms|s|m|h)
- --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s)
--help Print usage
- --host list Set one or more custom host-to-IP mappings (host:ip) (default [])
+ --host list Set one or more custom host-to-IP mappings (host:ip)
--hostname string Container hostname
- -l, --label list Service labels (default [])
- --limit-cpu decimal Limit CPUs (default 0.000)
+ -l, --label list Service labels
+ --limit-cpu decimal Limit CPUs
--limit-memory bytes Limit Memory
--log-driver string Logging driver for service
- --log-opt list Logging driver options (default [])
+ --log-opt list Logging driver options
--mode string Service mode (replicated or global) (default "replicated")
--mount mount Attach a filesystem mount to the service
--name string Service name
- --network list Network attachments (default [])
+ --network list Network attachments
--no-healthcheck Disable any container-specified HEALTHCHECK
--placement-pref pref Add a placement preference
-p, --publish port Publish a port as a node port
+ -q, --quiet Suppress progress output
--read-only Mount the container's root filesystem as read only
--replicas uint Number of tasks
- --reserve-cpu decimal Reserve CPUs (default 0.000)
+ --reserve-cpu decimal Reserve CPUs
--reserve-memory bytes Reserve Memory
- --restart-condition string Restart when condition is met ("none"|"on-failure"|"any")
- --restart-delay duration Delay between restart attempts (ns|us|ms|s|m|h)
+ --restart-condition string Restart when condition is met ("none"|"on-failure"|"any") (default "any")
+ --restart-delay duration Delay between restart attempts (ns|us|ms|s|m|h) (default 5s)
--restart-max-attempts uint Maximum number of restarts before giving up
--restart-window duration Window used to evaluate the restart policy (ns|us|ms|s|m|h)
--rollback-delay duration Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)
--rollback-failure-action string Action on rollback failure ("pause"|"continue") (default "pause")
- --rollback-max-failure-ratio float Failure rate to tolerate during a rollback
- --rollback-monitor duration Duration after each task rollback to monitor for failure
- (ns|us|ms|s|m|h) (default 0s)
+ --rollback-max-failure-ratio float Failure rate to tolerate during a rollback (default 0)
+ --rollback-monitor duration Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h) (default 5s)
--rollback-order string Rollback order ("start-first"|"stop-first") (default "stop-first")
- --rollback-parallelism uint Maximum number of tasks rolled back simultaneously (0 to roll
- back all at once) (default 1)
+ --rollback-parallelism uint Maximum number of tasks rolled back simultaneously (0 to roll back all at once) (default 1)
--secret secret Specify secrets to expose to the service
- --stop-grace-period duration Time to wait before force killing a container (ns|us|ms|s|m|h)
+ --stop-grace-period duration Time to wait before force killing a container (ns|us|ms|s|m|h) (default 10s)
--stop-signal string Signal to stop the container
-t, --tty Allocate a pseudo-TTY
--update-delay duration Delay between updates (ns|us|ms|s|m|h) (default 0s)
--update-failure-action string Action on update failure ("pause"|"continue"|"rollback") (default "pause")
- --update-max-failure-ratio float Failure rate to tolerate during an update
- --update-monitor duration Duration after each task update to monitor for failure (ns|us|ms|s|m|h)
+ --update-max-failure-ratio float Failure rate to tolerate during an update (default 0)
+ --update-monitor duration Duration after each task update to monitor for failure (ns|us|ms|s|m|h) (default 5s)
--update-order string Update order ("start-first"|"stop-first") (default "stop-first")
--update-parallelism uint Maximum number of tasks updated simultaneously (0 to update all at once) (default 1)
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
diff --git a/docs/reference/commandline/service_update.md b/docs/reference/commandline/service_update.md
index f79caeb41c..fae6b0af89 100644
--- a/docs/reference/commandline/service_update.md
+++ b/docs/reference/commandline/service_update.md
@@ -21,43 +21,43 @@ Usage: docker service update [OPTIONS] SERVICE
Update a service
Options:
- --args string Service command args
- --constraint-add list Add or update a placement constraint (default [])
- --constraint-rm list Remove a constraint (default [])
- --container-label-add list Add or update a container label (default [])
- --container-label-rm list Remove a container label by its key (default [])
- -d, --detach Exit immediately instead of waiting for the service to converge
- (default true)
- --dns-add list Add or update a custom DNS server (default [])
- --dns-option-add list Add or update a DNS option (default [])
- --dns-option-rm list Remove a DNS option (default [])
- --dns-rm list Remove a custom DNS server (default [])
- --dns-search-add list Add or update a custom DNS search domain (default [])
- --dns-search-rm list Remove a DNS search domain (default [])
- --endpoint-mode string Endpoint mode ("vip"|"dnsrr") (default "vip")
- --env-add list Add or update an environment variable (default [])
- --env-rm list Remove an environment variable (default [])
+ --args command Service command args
+ --constraint-add list Add or update a placement constraint
+ --constraint-rm list Remove a constraint
+ --container-label-add list Add or update a container label
+ --container-label-rm list Remove a container label by its key
+ -d, --detach Exit immediately instead of waiting for the service to converge (default true)
+ --dns-add list Add or update a custom DNS server
+ --dns-option-add list Add or update a DNS option
+ --dns-option-rm list Remove a DNS option
+ --dns-rm list Remove a custom DNS server
+ --dns-search-add list Add or update a custom DNS search domain
+ --dns-search-rm list Remove a DNS search domain
+ --endpoint-mode string Endpoint mode (vip or dnsrr)
+ --entrypoint command Overwrite the default ENTRYPOINT of the image
+ --env-add list Add or update an environment variable
+ --env-rm list Remove an environment variable
--force Force update even if no changes require it
- --group-add list Add an additional supplementary user group to the container (default [])
- --group-rm list Remove a previously added supplementary user group from the container (default [])
+ --group-add list Add an additional supplementary user group to the container
+ --group-rm list Remove a previously added supplementary user group from the container
--health-cmd string Command to run to check health
--health-interval duration Time between running the check (ns|us|ms|s|m|h)
--health-retries int Consecutive failures needed to report unhealthy
+ --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)
--health-timeout duration Maximum time to allow one check to run (ns|us|ms|s|m|h)
- --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s)
--help Print usage
- --host-add list Add or update a custom host-to-IP mapping (host:ip) (default [])
- --host-rm list Remove a custom host-to-IP mapping (host:ip) (default [])
+ --host-add list Add or update a custom host-to-IP mapping (host:ip)
+ --host-rm list Remove a custom host-to-IP mapping (host:ip)
--hostname string Container hostname
--image string Service image tag
- --label-add list Add or update a service label (default [])
- --label-rm list Remove a label by its key (default [])
- --limit-cpu decimal Limit CPUs (default 0.000)
+ --label-add list Add or update a service label
+ --label-rm list Remove a label by its key
+ --limit-cpu decimal Limit CPUs
--limit-memory bytes Limit Memory
--log-driver string Logging driver for service
- --log-opt list Logging driver options (default [])
+ --log-opt list Logging driver options
--mount-add mount Add or update a mount on a service
- --mount-rm list Remove a mount by its target path (default [])
+ --mount-rm list Remove a mount by its target path
--network-add list Add a network
--network-rm list Remove a network
--no-healthcheck Disable any container-specified HEALTHCHECK
@@ -65,34 +65,33 @@ Options:
--placement-pref-rm pref Remove a placement preference
--publish-add port Add or update a published port
--publish-rm port Remove a published port by its target port
+ -q, --quiet Suppress progress output
--read-only Mount the container's root filesystem as read only
--replicas uint Number of tasks
- --reserve-cpu decimal Reserve CPUs (default 0.000)
+ --reserve-cpu decimal Reserve CPUs
--reserve-memory bytes Reserve Memory
--restart-condition string Restart when condition is met ("none"|"on-failure"|"any")
--restart-delay duration Delay between restart attempts (ns|us|ms|s|m|h)
--restart-max-attempts uint Maximum number of restarts before giving up
--restart-window duration Window used to evaluate the restart policy (ns|us|ms|s|m|h)
--rollback Rollback to previous specification
- --rollback-delay duration Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)
- --rollback-failure-action string Action on rollback failure ("pause"|"continue") (default "pause")
+ --rollback-delay duration Delay between task rollbacks (ns|us|ms|s|m|h)
+ --rollback-failure-action string Action on rollback failure ("pause"|"continue")
--rollback-max-failure-ratio float Failure rate to tolerate during a rollback
- --rollback-monitor duration Duration after each task rollback to monitor for failure
- (ns|us|ms|s|m|h) (default 0s)
+ --rollback-monitor duration Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)
--rollback-order string Rollback order ("start-first"|"stop-first") (default "stop-first")
- --rollback-parallelism uint Maximum number of tasks rolled back simultaneously (0 to roll
- back all at once) (default 1)
+ --rollback-parallelism uint Maximum number of tasks rolled back simultaneously (0 to roll back all at once)
--secret-add secret Add or update a secret on a service
- --secret-rm list Remove a secret (default [])
+ --secret-rm list Remove a secret
--stop-grace-period duration Time to wait before force killing a container (ns|us|ms|s|m|h)
--stop-signal string Signal to stop the container
-t, --tty Allocate a pseudo-TTY
- --update-delay duration Delay between updates (ns|us|ms|s|m|h) (default 0s)
- --update-failure-action string Action on update failure ("pause"|"continue"|"rollback") (default "pause")
+ --update-delay duration Delay between updates (ns|us|ms|s|m|h)
+ --update-failure-action string Action on update failure ("pause"|"continue"|"rollback")
--update-max-failure-ratio float Failure rate to tolerate during an update
- --update-monitor duration Duration after each task update to monitor for failure (ns|us|ms|s|m|h)
- --update-order string Update order ("start-first"|"stop-first") (default "stop-first")
- --update-parallelism uint Maximum number of tasks updated simultaneously (0 to update all at once) (default 1)
+ --update-monitor duration Duration after each task update to monitor for failure (ns|us|ms|s|m|h)
+ --update-order string Update order ("start-first"|"stop-first")
+ --update-parallelism uint Maximum number of tasks updated simultaneously (0 to update all at once)
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
--with-registry-auth Send registry authentication details to swarm agents
-w, --workdir string Working directory inside the container
diff --git a/integration-cli/docker_api_swarm_service_test.go b/integration-cli/docker_api_swarm_service_test.go
index a96f684965..6a3c9f170b 100644
--- a/integration-cli/docker_api_swarm_service_test.go
+++ b/integration-cli/docker_api_swarm_service_test.go
@@ -60,6 +60,16 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *check.C) {
id := d.CreateService(c, simpleTestService, setInstances(instances))
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+ // insertDefaults inserts UpdateConfig when service is fetched by ID
+ _, out, err := d.SockRequest("GET", "/services/"+id+"?insertDefaults=true", nil)
+ c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+ c.Assert(string(out), checker.Contains, "UpdateConfig")
+
+ // insertDefaults inserts UpdateConfig when service is fetched by ID
+ _, out, err = d.SockRequest("GET", "/services/top?insertDefaults=true", nil)
+ c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+ c.Assert(string(out), checker.Contains, "UpdateConfig")
+
service := d.GetService(c, id)
instances = 5
d.UpdateService(c, service, setInstances(instances))
diff --git a/opts/opts.go b/opts/opts.go
index 8c82960c20..f76f308051 100644
--- a/opts/opts.go
+++ b/opts/opts.go
@@ -38,7 +38,10 @@ func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
}
func (opts *ListOpts) String() string {
- return fmt.Sprintf("%v", []string((*opts.values)))
+ if len(*opts.values) == 0 {
+ return ""
+ }
+ return fmt.Sprintf("%v", *opts.values)
}
// Set validates if needed the input value and adds it to the
@@ -343,6 +346,9 @@ type NanoCPUs int64
// String returns the string format of the number
func (c *NanoCPUs) String() string {
+ if *c == 0 {
+ return ""
+ }
return big.NewRat(c.Value(), 1e9).FloatString(3)
}
diff --git a/opts/opts_test.go b/opts/opts_test.go
index e137127156..c1e7735b58 100644
--- a/opts/opts_test.go
+++ b/opts/opts_test.go
@@ -93,12 +93,12 @@ func TestListOptsWithValidator(t *testing.T) {
// Re-using logOptsvalidator (used by MapOpts)
o := NewListOpts(logOptsValidator)
o.Set("foo")
- if o.String() != "[]" {
- t.Errorf("%s != []", o.String())
+ if o.String() != "" {
+ t.Errorf(`%s != ""`, o.String())
}
o.Set("foo=bar")
- if o.String() != "[]" {
- t.Errorf("%s != []", o.String())
+ if o.String() != "" {
+ t.Errorf(`%s != ""`, o.String())
}
o.Set("max-file=2")
if o.Len() != 1 {
@@ -111,8 +111,8 @@ func TestListOptsWithValidator(t *testing.T) {
t.Error("o.Get(\"baz\") == true")
}
o.Delete("max-file=2")
- if o.String() != "[]" {
- t.Errorf("%s != []", o.String())
+ if o.String() != "" {
+ t.Errorf(`%s != ""`, o.String())
}
}