Browse code

Merge pull request #32284 from aaronlehmann/fix-service-defaults

Improve default handling for "service create"

Sebastiaan van Stijn authored on 2017/04/11 20:06:53
Showing 26 changed files
... ...
@@ -17,7 +17,7 @@ type Backend interface {
17 17
 	GetUnlockKey() (string, error)
18 18
 	UnlockSwarm(req types.UnlockRequest) error
19 19
 	GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
20
-	GetService(string) (types.Service, error)
20
+	GetService(idOrName string, insertDefaults bool) (types.Service, error)
21 21
 	CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
22 22
 	UpdateService(string, uint64, types.ServiceSpec, basictypes.ServiceUpdateOptions) (*basictypes.ServiceUpdateResponse, error)
23 23
 	RemoveService(string) error
... ...
@@ -30,7 +30,7 @@ type Backend interface {
30 30
 	GetTask(string) (types.Task, error)
31 31
 	GetSecrets(opts basictypes.SecretListOptions) ([]types.Secret, error)
32 32
 	CreateSecret(s types.SecretSpec) (string, error)
33
-	RemoveSecret(id string) error
33
+	RemoveSecret(idOrName string) error
34 34
 	GetSecret(id string) (types.Secret, error)
35
-	UpdateSecret(id string, version uint64, spec types.SecretSpec) error
35
+	UpdateSecret(idOrName string, version uint64, spec types.SecretSpec) error
36 36
 }
... ...
@@ -151,7 +151,17 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
151 151
 }
152 152
 
153 153
 func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
154
-	service, err := sr.backend.GetService(vars["id"])
154
+	var insertDefaults bool
155
+	if value := r.URL.Query().Get("insertDefaults"); value != "" {
156
+		var err error
157
+		insertDefaults, err = strconv.ParseBool(value)
158
+		if err != nil {
159
+			err := fmt.Errorf("invalid value for insertDefaults: %s", value)
160
+			return errors.NewBadRequestError(err)
161
+		}
162
+	}
163
+
164
+	service, err := sr.backend.GetService(vars["id"], insertDefaults)
155 165
 	if err != nil {
156 166
 		logrus.Errorf("Error getting service %s: %v", vars["id"], err)
157 167
 		return err
... ...
@@ -39,7 +39,7 @@ func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *
39 39
 	// checking for whether logs are TTY involves iterating over every service
40 40
 	// and task. idk if there is a better way
41 41
 	for _, service := range selector.Services {
42
-		s, err := sr.backend.GetService(service)
42
+		s, err := sr.backend.GetService(service, false)
43 43
 		if err != nil {
44 44
 			// maybe should return some context with this error?
45 45
 			return err
... ...
@@ -7584,6 +7584,11 @@ paths:
7584 7584
           description: "ID or name of service."
7585 7585
           required: true
7586 7586
           type: "string"
7587
+        - name: "insertDefaults"
7588
+          in: "query"
7589
+          description: "Fill empty fields with default values."
7590
+          type: "boolean"
7591
+          default: false
7587 7592
       tags: ["Service"]
7588 7593
     delete:
7589 7594
       summary: "Delete a service"
... ...
@@ -316,12 +316,18 @@ type ServiceUpdateOptions struct {
316 316
 	Rollback string
317 317
 }
318 318
 
319
-// ServiceListOptions holds parameters to list  services with.
319
+// ServiceListOptions holds parameters to list services with.
320 320
 type ServiceListOptions struct {
321 321
 	Filters filters.Args
322 322
 }
323 323
 
324
-// TaskListOptions holds parameters to list  tasks with.
324
+// ServiceInspectOptions holds parameters related to the "service inspect"
325
+// operation.
326
+type ServiceInspectOptions struct {
327
+	InsertDefaults bool
328
+}
329
+
330
+// TaskListOptions holds parameters to list tasks with.
325 331
 type TaskListOptions struct {
326 332
 	Filters filters.Args
327 333
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package idresolver
2 2
 
3 3
 import (
4
+	"github.com/docker/docker/api/types"
4 5
 	"github.com/docker/docker/api/types/swarm"
5 6
 	"github.com/docker/docker/client"
6 7
 	"golang.org/x/net/context"
... ...
@@ -19,7 +20,7 @@ func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (s
19 19
 	return swarm.Node{}, []byte{}, nil
20 20
 }
21 21
 
22
-func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) {
22
+func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) {
23 23
 	if cli.serviceInspectFunc != nil {
24 24
 		return cli.serviceInspectFunc(serviceID)
25 25
 	}
... ...
@@ -3,6 +3,7 @@ package idresolver
3 3
 import (
4 4
 	"golang.org/x/net/context"
5 5
 
6
+	"github.com/docker/docker/api/types"
6 7
 	"github.com/docker/docker/api/types/swarm"
7 8
 	"github.com/docker/docker/client"
8 9
 	"github.com/pkg/errors"
... ...
@@ -39,7 +40,7 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string,
39 39
 		}
40 40
 		return id, nil
41 41
 	case swarm.Service:
42
-		service, _, err := r.client.ServiceInspectWithRaw(ctx, id)
42
+		service, _, err := r.client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
43 43
 		if err != nil {
44 44
 			return id, nil
45 45
 		}
... ...
@@ -30,7 +30,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
30 30
 	flags.StringVar(&opts.mode, flagMode, "replicated", "Service mode (replicated or global)")
31 31
 	flags.StringVar(&opts.name, flagName, "", "Service name")
32 32
 
33
-	addServiceFlags(flags, opts)
33
+	addServiceFlags(flags, opts, buildServiceDefaultFlagMapping())
34 34
 
35 35
 	flags.VarP(&opts.labels, flagLabel, "l", "Service labels")
36 36
 	flags.Var(&opts.containerLabels, flagContainerLabel, "Container labels")
... ...
@@ -65,7 +65,7 @@ func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *service
65 65
 
66 66
 	ctx := context.Background()
67 67
 
68
-	service, err := opts.ToService(ctx, apiClient)
68
+	service, err := opts.ToService(ctx, apiClient, flags)
69 69
 	if err != nil {
70 70
 		return err
71 71
 	}
... ...
@@ -5,6 +5,7 @@ import (
5 5
 
6 6
 	"golang.org/x/net/context"
7 7
 
8
+	"github.com/docker/docker/api/types"
8 9
 	"github.com/docker/docker/cli"
9 10
 	"github.com/docker/docker/cli/command"
10 11
 	"github.com/docker/docker/cli/command/formatter"
... ...
@@ -51,7 +52,8 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
51 51
 	}
52 52
 
53 53
 	getRef := func(ref string) (interface{}, []byte, error) {
54
-		service, _, err := client.ServiceInspectWithRaw(ctx, ref)
54
+		// Service inspect shows defaults values in empty fields.
55
+		service, _, err := client.ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
55 56
 		if err == nil || !apiclient.IsErrServiceNotFound(err) {
56 57
 			return service, nil, err
57 58
 		}
... ...
@@ -86,7 +86,7 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
86 86
 		tty          bool
87 87
 	)
88 88
 
89
-	service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target)
89
+	service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target, types.ServiceInspectOptions{})
90 90
 	if err != nil {
91 91
 		// if it's any error other than service not found, it's Real
92 92
 		if !client.IsErrServiceNotFound(err) {
... ...
@@ -12,7 +12,10 @@ import (
12 12
 	"github.com/docker/docker/client"
13 13
 	"github.com/docker/docker/opts"
14 14
 	runconfigopts "github.com/docker/docker/runconfig/opts"
15
+	"github.com/docker/swarmkit/api"
16
+	"github.com/docker/swarmkit/api/defaults"
15 17
 	shlex "github.com/flynn-archive/go-shlex"
18
+	gogotypes "github.com/gogo/protobuf/types"
16 19
 	"github.com/pkg/errors"
17 20
 	"github.com/spf13/pflag"
18 21
 	"golang.org/x/net/context"
... ...
@@ -177,6 +180,9 @@ func (s *ShlexOpt) Type() string {
177 177
 }
178 178
 
179 179
 func (s *ShlexOpt) String() string {
180
+	if len(*s) == 0 {
181
+		return ""
182
+	}
180 183
 	return fmt.Sprint(*s)
181 184
 }
182 185
 
... ...
@@ -194,15 +200,75 @@ type updateOptions struct {
194 194
 	order           string
195 195
 }
196 196
 
197
-func (opts updateOptions) config() *swarm.UpdateConfig {
197
+func updateConfigFromDefaults(defaultUpdateConfig *api.UpdateConfig) *swarm.UpdateConfig {
198
+	defaultFailureAction := strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaultUpdateConfig.FailureAction)])
199
+	defaultMonitor, _ := gogotypes.DurationFromProto(defaultUpdateConfig.Monitor)
198 200
 	return &swarm.UpdateConfig{
199
-		Parallelism:     opts.parallelism,
200
-		Delay:           opts.delay,
201
-		Monitor:         opts.monitor,
202
-		FailureAction:   opts.onFailure,
203
-		MaxFailureRatio: opts.maxFailureRatio.Value(),
204
-		Order:           opts.order,
201
+		Parallelism:     defaultUpdateConfig.Parallelism,
202
+		Delay:           defaultUpdateConfig.Delay,
203
+		Monitor:         defaultMonitor,
204
+		FailureAction:   defaultFailureAction,
205
+		MaxFailureRatio: defaultUpdateConfig.MaxFailureRatio,
206
+		Order:           defaultOrder(defaultUpdateConfig.Order),
207
+	}
208
+}
209
+
210
+func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
211
+	if !anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
212
+		return nil
213
+	}
214
+
215
+	updateConfig := updateConfigFromDefaults(defaults.Service.Update)
216
+
217
+	if flags.Changed(flagUpdateParallelism) {
218
+		updateConfig.Parallelism = opts.parallelism
219
+	}
220
+	if flags.Changed(flagUpdateDelay) {
221
+		updateConfig.Delay = opts.delay
222
+	}
223
+	if flags.Changed(flagUpdateMonitor) {
224
+		updateConfig.Monitor = opts.monitor
225
+	}
226
+	if flags.Changed(flagUpdateFailureAction) {
227
+		updateConfig.FailureAction = opts.onFailure
228
+	}
229
+	if flags.Changed(flagUpdateMaxFailureRatio) {
230
+		updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
231
+	}
232
+	if flags.Changed(flagUpdateOrder) {
233
+		updateConfig.Order = opts.order
234
+	}
235
+
236
+	return updateConfig
237
+}
238
+
239
+func (opts updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
240
+	if !anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio) {
241
+		return nil
242
+	}
243
+
244
+	updateConfig := updateConfigFromDefaults(defaults.Service.Rollback)
245
+
246
+	if flags.Changed(flagRollbackParallelism) {
247
+		updateConfig.Parallelism = opts.parallelism
248
+	}
249
+	if flags.Changed(flagRollbackDelay) {
250
+		updateConfig.Delay = opts.delay
251
+	}
252
+	if flags.Changed(flagRollbackMonitor) {
253
+		updateConfig.Monitor = opts.monitor
254
+	}
255
+	if flags.Changed(flagRollbackFailureAction) {
256
+		updateConfig.FailureAction = opts.onFailure
205 257
 	}
258
+	if flags.Changed(flagRollbackMaxFailureRatio) {
259
+		updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
260
+	}
261
+	if flags.Changed(flagRollbackOrder) {
262
+		updateConfig.Order = opts.order
263
+	}
264
+
265
+	return updateConfig
206 266
 }
207 267
 
208 268
 type resourceOptions struct {
... ...
@@ -232,13 +298,70 @@ type restartPolicyOptions struct {
232 232
 	window      DurationOpt
233 233
 }
234 234
 
235
-func (r *restartPolicyOptions) ToRestartPolicy() *swarm.RestartPolicy {
236
-	return &swarm.RestartPolicy{
237
-		Condition:   swarm.RestartPolicyCondition(r.condition),
238
-		Delay:       r.delay.Value(),
239
-		MaxAttempts: r.maxAttempts.Value(),
240
-		Window:      r.window.Value(),
235
+func defaultRestartPolicy() *swarm.RestartPolicy {
236
+	defaultMaxAttempts := defaults.Service.Task.Restart.MaxAttempts
237
+	rp := &swarm.RestartPolicy{
238
+		MaxAttempts: &defaultMaxAttempts,
239
+	}
240
+
241
+	if defaults.Service.Task.Restart.Delay != nil {
242
+		defaultRestartDelay, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
243
+		rp.Delay = &defaultRestartDelay
244
+	}
245
+	if defaults.Service.Task.Restart.Window != nil {
246
+		defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
247
+		rp.Window = &defaultRestartWindow
248
+	}
249
+	rp.Condition = defaultRestartCondition()
250
+
251
+	return rp
252
+}
253
+
254
+func defaultRestartCondition() swarm.RestartPolicyCondition {
255
+	switch defaults.Service.Task.Restart.Condition {
256
+	case api.RestartOnNone:
257
+		return "none"
258
+	case api.RestartOnFailure:
259
+		return "on-failure"
260
+	case api.RestartOnAny:
261
+		return "any"
262
+	default:
263
+		return ""
264
+	}
265
+}
266
+
267
+func defaultOrder(order api.UpdateConfig_UpdateOrder) string {
268
+	switch order {
269
+	case api.UpdateConfig_STOP_FIRST:
270
+		return "stop-first"
271
+	case api.UpdateConfig_START_FIRST:
272
+		return "start-first"
273
+	default:
274
+		return ""
275
+	}
276
+}
277
+
278
+func (r *restartPolicyOptions) ToRestartPolicy(flags *pflag.FlagSet) *swarm.RestartPolicy {
279
+	if !anyChanged(flags, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow, flagRestartCondition) {
280
+		return nil
281
+	}
282
+
283
+	restartPolicy := defaultRestartPolicy()
284
+
285
+	if flags.Changed(flagRestartDelay) {
286
+		restartPolicy.Delay = r.delay.Value()
287
+	}
288
+	if flags.Changed(flagRestartCondition) {
289
+		restartPolicy.Condition = swarm.RestartPolicyCondition(r.condition)
241 290
 	}
291
+	if flags.Changed(flagRestartMaxAttempts) {
292
+		restartPolicy.MaxAttempts = r.maxAttempts.Value()
293
+	}
294
+	if flags.Changed(flagRestartWindow) {
295
+		restartPolicy.Window = r.window.Value()
296
+	}
297
+
298
+	return restartPolicy
242 299
 }
243 300
 
244 301
 type credentialSpecOpt struct {
... ...
@@ -463,7 +586,14 @@ func (opts *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
463 463
 	return serviceMode, nil
464 464
 }
465 465
 
466
-func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIClient) (swarm.ServiceSpec, error) {
466
+func (opts *serviceOptions) ToStopGracePeriod(flags *pflag.FlagSet) *time.Duration {
467
+	if flags.Changed(flagStopGracePeriod) {
468
+		return opts.stopGrace.Value()
469
+	}
470
+	return nil
471
+}
472
+
473
+func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIClient, flags *pflag.FlagSet) (swarm.ServiceSpec, error) {
467 474
 	var service swarm.ServiceSpec
468 475
 
469 476
 	envVariables, err := runconfigopts.ReadKVStrings(opts.envFile.GetAll(), opts.env.GetAll())
... ...
@@ -526,13 +656,13 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
526 526
 					Options:     opts.dnsOption.GetAll(),
527 527
 				},
528 528
 				Hosts:           convertExtraHostsToSwarmHosts(opts.hosts.GetAll()),
529
-				StopGracePeriod: opts.stopGrace.Value(),
529
+				StopGracePeriod: opts.ToStopGracePeriod(flags),
530 530
 				Secrets:         nil,
531 531
 				Healthcheck:     healthConfig,
532 532
 			},
533 533
 			Networks:      networks,
534 534
 			Resources:     opts.resources.ToResourceRequirements(),
535
-			RestartPolicy: opts.restartPolicy.ToRestartPolicy(),
535
+			RestartPolicy: opts.restartPolicy.ToRestartPolicy(flags),
536 536
 			Placement: &swarm.Placement{
537 537
 				Constraints: opts.constraints.GetAll(),
538 538
 				Preferences: opts.placementPrefs.prefs,
... ...
@@ -540,8 +670,8 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
540 540
 			LogDriver: opts.logDriver.toLogDriver(),
541 541
 		},
542 542
 		Mode:           serviceMode,
543
-		UpdateConfig:   opts.update.config(),
544
-		RollbackConfig: opts.rollback.config(),
543
+		UpdateConfig:   opts.update.updateConfig(flags),
544
+		RollbackConfig: opts.update.rollbackConfig(flags),
545 545
 		EndpointSpec:   opts.endpoint.ToEndpointSpec(),
546 546
 	}
547 547
 
... ...
@@ -554,9 +684,67 @@ func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIC
554 554
 	return service, nil
555 555
 }
556 556
 
557
+type flagDefaults map[string]interface{}
558
+
559
+func (fd flagDefaults) getUint64(flagName string) uint64 {
560
+	if val, ok := fd[flagName].(uint64); ok {
561
+		return val
562
+	}
563
+	return 0
564
+}
565
+
566
+func (fd flagDefaults) getString(flagName string) string {
567
+	if val, ok := fd[flagName].(string); ok {
568
+		return val
569
+	}
570
+	return ""
571
+}
572
+
573
+func buildServiceDefaultFlagMapping() flagDefaults {
574
+	defaultFlagValues := make(map[string]interface{})
575
+
576
+	defaultFlagValues[flagStopGracePeriod], _ = gogotypes.DurationFromProto(defaults.Service.Task.GetContainer().StopGracePeriod)
577
+	defaultFlagValues[flagRestartCondition] = `"` + defaultRestartCondition() + `"`
578
+	defaultFlagValues[flagRestartDelay], _ = gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
579
+
580
+	if defaults.Service.Task.Restart.MaxAttempts != 0 {
581
+		defaultFlagValues[flagRestartMaxAttempts] = defaults.Service.Task.Restart.MaxAttempts
582
+	}
583
+
584
+	defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
585
+	if defaultRestartWindow != 0 {
586
+		defaultFlagValues[flagRestartWindow] = defaultRestartWindow
587
+	}
588
+
589
+	defaultFlagValues[flagUpdateParallelism] = defaults.Service.Update.Parallelism
590
+	defaultFlagValues[flagUpdateDelay] = defaults.Service.Update.Delay
591
+	defaultFlagValues[flagUpdateMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Update.Monitor)
592
+	defaultFlagValues[flagUpdateFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Update.FailureAction)]) + `"`
593
+	defaultFlagValues[flagUpdateMaxFailureRatio] = defaults.Service.Update.MaxFailureRatio
594
+	defaultFlagValues[flagUpdateOrder] = `"` + defaultOrder(defaults.Service.Update.Order) + `"`
595
+
596
+	defaultFlagValues[flagRollbackParallelism] = defaults.Service.Rollback.Parallelism
597
+	defaultFlagValues[flagRollbackDelay] = defaults.Service.Rollback.Delay
598
+	defaultFlagValues[flagRollbackMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Rollback.Monitor)
599
+	defaultFlagValues[flagRollbackFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Rollback.FailureAction)]) + `"`
600
+	defaultFlagValues[flagRollbackMaxFailureRatio] = defaults.Service.Rollback.MaxFailureRatio
601
+	defaultFlagValues[flagRollbackOrder] = `"` + defaultOrder(defaults.Service.Rollback.Order) + `"`
602
+
603
+	defaultFlagValues[flagEndpointMode] = "vip"
604
+
605
+	return defaultFlagValues
606
+}
607
+
557 608
 // addServiceFlags adds all flags that are common to both `create` and `update`.
558 609
 // Any flags that are not common are added separately in the individual command
559
-func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
610
+func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValues flagDefaults) {
611
+	flagDesc := func(flagName string, desc string) string {
612
+		if defaultValue, ok := defaultFlagValues[flagName]; ok {
613
+			return fmt.Sprintf("%s (default %v)", desc, defaultValue)
614
+		}
615
+		return desc
616
+	}
617
+
560 618
 	flags.BoolVarP(&opts.detach, "detach", "d", true, "Exit immediately instead of waiting for the service to converge")
561 619
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress progress output")
562 620
 
... ...
@@ -572,39 +760,40 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
572 572
 	flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
573 573
 	flags.Var(&opts.resources.resCPU, flagReserveCPU, "Reserve CPUs")
574 574
 	flags.Var(&opts.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
575
-	flags.Var(&opts.stopGrace, flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)")
576 575
 
576
+	flags.Var(&opts.stopGrace, flagStopGracePeriod, flagDesc(flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)"))
577 577
 	flags.Var(&opts.replicas, flagReplicas, "Number of tasks")
578 578
 
579
-	flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", `Restart when condition is met ("none"|"on-failure"|"any")`)
580
-	flags.Var(&opts.restartPolicy.delay, flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)")
581
-	flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, "Maximum number of restarts before giving up")
582
-	flags.Var(&opts.restartPolicy.window, flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)")
579
+	flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", flagDesc(flagRestartCondition, `Restart when condition is met ("none"|"on-failure"|"any")`))
580
+	flags.Var(&opts.restartPolicy.delay, flagRestartDelay, flagDesc(flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)"))
581
+	flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, flagDesc(flagRestartMaxAttempts, "Maximum number of restarts before giving up"))
582
+
583
+	flags.Var(&opts.restartPolicy.window, flagRestartWindow, flagDesc(flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)"))
583 584
 
584
-	flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, 1, "Maximum number of tasks updated simultaneously (0 to update all at once)")
585
-	flags.DurationVar(&opts.update.delay, flagUpdateDelay, time.Duration(0), "Delay between updates (ns|us|ms|s|m|h) (default 0s)")
586
-	flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, time.Duration(0), "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)")
585
+	flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, defaultFlagValues.getUint64(flagUpdateParallelism), "Maximum number of tasks updated simultaneously (0 to update all at once)")
586
+	flags.DurationVar(&opts.update.delay, flagUpdateDelay, 0, flagDesc(flagUpdateDelay, "Delay between updates (ns|us|ms|s|m|h)"))
587
+	flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, 0, flagDesc(flagUpdateMonitor, "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)"))
587 588
 	flags.SetAnnotation(flagUpdateMonitor, "version", []string{"1.25"})
588
-	flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "pause", `Action on update failure ("pause"|"continue"|"rollback")`)
589
-	flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update")
589
+	flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "", flagDesc(flagUpdateFailureAction, `Action on update failure ("pause"|"continue"|"rollback")`))
590
+	flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, flagDesc(flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update"))
590 591
 	flags.SetAnnotation(flagUpdateMaxFailureRatio, "version", []string{"1.25"})
591
-	flags.StringVar(&opts.update.order, flagUpdateOrder, "stop-first", `Update order ("start-first"|"stop-first")`)
592
+	flags.StringVar(&opts.update.order, flagUpdateOrder, "", flagDesc(flagUpdateOrder, `Update order ("start-first"|"stop-first")`))
592 593
 	flags.SetAnnotation(flagUpdateOrder, "version", []string{"1.29"})
593 594
 
594
-	flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, 1, "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
595
+	flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, defaultFlagValues.getUint64(flagRollbackParallelism), "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
595 596
 	flags.SetAnnotation(flagRollbackParallelism, "version", []string{"1.28"})
596
-	flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, time.Duration(0), "Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)")
597
+	flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, 0, flagDesc(flagRollbackDelay, "Delay between task rollbacks (ns|us|ms|s|m|h)"))
597 598
 	flags.SetAnnotation(flagRollbackDelay, "version", []string{"1.28"})
598
-	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)")
599
+	flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, 0, flagDesc(flagRollbackMonitor, "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)"))
599 600
 	flags.SetAnnotation(flagRollbackMonitor, "version", []string{"1.28"})
600
-	flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "pause", `Action on rollback failure ("pause"|"continue")`)
601
+	flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "", flagDesc(flagRollbackFailureAction, `Action on rollback failure ("pause"|"continue")`))
601 602
 	flags.SetAnnotation(flagRollbackFailureAction, "version", []string{"1.28"})
602
-	flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback")
603
+	flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, flagDesc(flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback"))
603 604
 	flags.SetAnnotation(flagRollbackMaxFailureRatio, "version", []string{"1.28"})
604
-	flags.StringVar(&opts.rollback.order, flagRollbackOrder, "stop-first", `Rollback order ("start-first"|"stop-first")`)
605
+	flags.StringVar(&opts.rollback.order, flagRollbackOrder, "", flagDesc(flagRollbackOrder, `Rollback order ("start-first"|"stop-first")`))
605 606
 	flags.SetAnnotation(flagRollbackOrder, "version", []string{"1.29"})
606 607
 
607
-	flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "vip", "Endpoint mode (vip or dnsrr)")
608
+	flags.StringVar(&opts.endpoint.mode, flagEndpointMode, defaultFlagValues.getString(flagEndpointMode), "Endpoint mode (vip or dnsrr)")
608 609
 
609 610
 	flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to swarm agents")
610 611
 
... ...
@@ -85,7 +85,7 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
85 85
 	)
86 86
 
87 87
 	for {
88
-		service, _, err := client.ServiceInspectWithRaw(ctx, serviceID)
88
+		service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
89 89
 		if err != nil {
90 90
 			return err
91 91
 		}
... ...
@@ -71,7 +71,7 @@ func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint6
71 71
 	client := dockerCli.Client()
72 72
 	ctx := context.Background()
73 73
 
74
-	service, _, err := client.ServiceInspectWithRaw(ctx, serviceID)
74
+	service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
75 75
 	if err != nil {
76 76
 		return err
77 77
 	}
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	"github.com/docker/docker/opts"
18 18
 	runconfigopts "github.com/docker/docker/runconfig/opts"
19 19
 	"github.com/docker/go-connections/nat"
20
+	"github.com/docker/swarmkit/api/defaults"
20 21
 	"github.com/pkg/errors"
21 22
 	"github.com/spf13/cobra"
22 23
 	"github.com/spf13/pflag"
... ...
@@ -42,7 +43,7 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
42 42
 	flags.SetAnnotation("rollback", "version", []string{"1.25"})
43 43
 	flags.Bool("force", false, "Force update even if no changes require it")
44 44
 	flags.SetAnnotation("force", "version", []string{"1.25"})
45
-	addServiceFlags(flags, serviceOpts)
45
+	addServiceFlags(flags, serviceOpts, nil)
46 46
 
47 47
 	flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
48 48
 	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
101 101
 	apiClient := dockerCli.Client()
102 102
 	ctx := context.Background()
103 103
 
104
-	service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID)
104
+	service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
105 105
 	if err != nil {
106 106
 		return err
107 107
 	}
... ...
@@ -294,9 +295,8 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
294 294
 
295 295
 	if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
296 296
 		if task.RestartPolicy == nil {
297
-			task.RestartPolicy = &swarm.RestartPolicy{}
297
+			task.RestartPolicy = defaultRestartPolicy()
298 298
 		}
299
-
300 299
 		if flags.Changed(flagRestartCondition) {
301 300
 			value, _ := flags.GetString(flagRestartCondition)
302 301
 			task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
... ...
@@ -332,7 +332,7 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
332 332
 
333 333
 	if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio, flagUpdateOrder) {
334 334
 		if spec.UpdateConfig == nil {
335
-			spec.UpdateConfig = &swarm.UpdateConfig{}
335
+			spec.UpdateConfig = updateConfigFromDefaults(defaults.Service.Update)
336 336
 		}
337 337
 		updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
338 338
 		updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
... ...
@@ -344,7 +344,7 @@ func updateService(ctx context.Context, apiClient client.APIClient, flags *pflag
344 344
 
345 345
 	if anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio, flagRollbackOrder) {
346 346
 		if spec.RollbackConfig == nil {
347
-			spec.RollbackConfig = &swarm.UpdateConfig{}
347
+			spec.RollbackConfig = updateConfigFromDefaults(defaults.Service.Rollback)
348 348
 		}
349 349
 		updateUint64(flagRollbackParallelism, &spec.RollbackConfig.Parallelism)
350 350
 		updateDuration(flagRollbackDelay, &spec.RollbackConfig.Delay)
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	"github.com/docker/docker/api/types"
7 8
 	"github.com/docker/docker/cli"
8 9
 	"github.com/docker/docker/cli/command"
9 10
 	"github.com/docker/docker/cli/command/inspect"
... ...
@@ -79,7 +80,8 @@ func inspectNode(ctx context.Context, dockerCli *command.DockerCli) inspect.GetR
79 79
 
80 80
 func inspectService(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
81 81
 	return func(ref string) (interface{}, []byte, error) {
82
-		return dockerCli.Client().ServiceInspectWithRaw(ctx, ref)
82
+		// Service inspect shows defaults values in empty fields.
83
+		return dockerCli.Client().ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
83 84
 	}
84 85
 }
85 86
 
... ...
@@ -123,7 +123,7 @@ type PluginAPIClient interface {
123 123
 // ServiceAPIClient defines API client methods for the services
124 124
 type ServiceAPIClient interface {
125 125
 	ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error)
126
-	ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error)
126
+	ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
127 127
 	ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
128 128
 	ServiceRemove(ctx context.Context, serviceID string) error
129 129
 	ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
... ...
@@ -3,16 +3,21 @@ package client
3 3
 import (
4 4
 	"bytes"
5 5
 	"encoding/json"
6
+	"fmt"
6 7
 	"io/ioutil"
7 8
 	"net/http"
9
+	"net/url"
8 10
 
11
+	"github.com/docker/docker/api/types"
9 12
 	"github.com/docker/docker/api/types/swarm"
10 13
 	"golang.org/x/net/context"
11 14
 )
12 15
 
13 16
 // ServiceInspectWithRaw returns the service information and the raw data.
14
-func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) {
15
-	serverResp, err := cli.get(ctx, "/services/"+serviceID, nil, nil)
17
+func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) {
18
+	query := url.Values{}
19
+	query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
20
+	serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
16 21
 	if err != nil {
17 22
 		if serverResp.statusCode == http.StatusNotFound {
18 23
 			return swarm.Service{}, nil, serviceNotFoundError{serviceID}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"strings"
10 10
 	"testing"
11 11
 
12
+	"github.com/docker/docker/api/types"
12 13
 	"github.com/docker/docker/api/types/swarm"
13 14
 	"golang.org/x/net/context"
14 15
 )
... ...
@@ -18,7 +19,7 @@ func TestServiceInspectError(t *testing.T) {
18 18
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
19 19
 	}
20 20
 
21
-	_, _, err := client.ServiceInspectWithRaw(context.Background(), "nothing")
21
+	_, _, err := client.ServiceInspectWithRaw(context.Background(), "nothing", types.ServiceInspectOptions{})
22 22
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
23 23
 		t.Fatalf("expected a Server Error, got %v", err)
24 24
 	}
... ...
@@ -29,7 +30,7 @@ func TestServiceInspectServiceNotFound(t *testing.T) {
29 29
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
30 30
 	}
31 31
 
32
-	_, _, err := client.ServiceInspectWithRaw(context.Background(), "unknown")
32
+	_, _, err := client.ServiceInspectWithRaw(context.Background(), "unknown", types.ServiceInspectOptions{})
33 33
 	if err == nil || !IsErrServiceNotFound(err) {
34 34
 		t.Fatalf("expected a serviceNotFoundError error, got %v", err)
35 35
 	}
... ...
@@ -55,7 +56,7 @@ func TestServiceInspect(t *testing.T) {
55 55
 		}),
56 56
 	}
57 57
 
58
-	serviceInspect, _, err := client.ServiceInspectWithRaw(context.Background(), "service_id")
58
+	serviceInspect, _, err := client.ServiceInspectWithRaw(context.Background(), "service_id", types.ServiceInspectOptions{})
59 59
 	if err != nil {
60 60
 		t.Fatal(err)
61 61
 	}
... ...
@@ -58,9 +58,9 @@ func getNode(ctx context.Context, c swarmapi.ControlClient, input string) (*swar
58 58
 	return rl.Nodes[0], nil
59 59
 }
60 60
 
61
-func getService(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Service, error) {
61
+func getService(ctx context.Context, c swarmapi.ControlClient, input string, insertDefaults bool) (*swarmapi.Service, error) {
62 62
 	// GetService to match via full ID.
63
-	if rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: input}); err == nil {
63
+	if rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: input, InsertDefaults: insertDefaults}); err == nil {
64 64
 		return rg.Service, nil
65 65
 	}
66 66
 
... ...
@@ -91,7 +91,15 @@ func getService(ctx context.Context, c swarmapi.ControlClient, input string) (*s
91 91
 		return nil, fmt.Errorf("service %s is ambiguous (%d matches found)", input, l)
92 92
 	}
93 93
 
94
-	return rl.Services[0], nil
94
+	if !insertDefaults {
95
+		return rl.Services[0], nil
96
+	}
97
+
98
+	rg, err := c.GetService(ctx, &swarmapi.GetServiceRequest{ServiceID: rl.Services[0].ID, InsertDefaults: true})
99
+	if err == nil {
100
+		return rg.Service, nil
101
+	}
102
+	return nil, err
95 103
 }
96 104
 
97 105
 func getTask(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Task, error) {
... ...
@@ -87,10 +87,10 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
87 87
 }
88 88
 
89 89
 // GetService returns a service based on an ID or name.
90
-func (c *Cluster) GetService(input string) (types.Service, error) {
90
+func (c *Cluster) GetService(input string, insertDefaults bool) (types.Service, error) {
91 91
 	var service *swarmapi.Service
92 92
 	if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
93
-		s, err := getService(ctx, state.controlClient, input)
93
+		s, err := getService(ctx, state.controlClient, input, insertDefaults)
94 94
 		if err != nil {
95 95
 			return err
96 96
 		}
... ...
@@ -187,7 +187,7 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
187 187
 			return apierrors.NewBadRequestError(err)
188 188
 		}
189 189
 
190
-		currentService, err := getService(ctx, state.controlClient, serviceIDOrName)
190
+		currentService, err := getService(ctx, state.controlClient, serviceIDOrName, false)
191 191
 		if err != nil {
192 192
 			return err
193 193
 		}
... ...
@@ -289,7 +289,7 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
289 289
 // RemoveService removes a service from a managed swarm cluster.
290 290
 func (c *Cluster) RemoveService(input string) error {
291 291
 	return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
292
-		service, err := getService(ctx, state.controlClient, input)
292
+		service, err := getService(ctx, state.controlClient, input, false)
293 293
 		if err != nil {
294 294
 			return err
295 295
 		}
... ...
@@ -442,7 +442,7 @@ func convertSelector(ctx context.Context, cc swarmapi.ControlClient, selector *b
442 442
 	// don't rely on swarmkit to resolve IDs, do it ourselves
443 443
 	swarmSelector := &swarmapi.LogSelector{}
444 444
 	for _, s := range selector.Services {
445
-		service, err := getService(ctx, cc, s)
445
+		service, err := getService(ctx, cc, s, false)
446 446
 		if err != nil {
447 447
 			return nil, err
448 448
 		}
... ...
@@ -23,7 +23,7 @@ func (c *Cluster) GetTasks(options apitypes.TaskListOptions) ([]types.Task, erro
23 23
 		if filter.Include("service") {
24 24
 			serviceFilters := filter.Get("service")
25 25
 			for _, serviceFilter := range serviceFilters {
26
-				service, err := c.GetService(serviceFilter)
26
+				service, err := c.GetService(serviceFilter, false)
27 27
 				if err != nil {
28 28
 					return err
29 29
 				}
... ...
@@ -21,61 +21,60 @@ Usage:  docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
21 21
 Create a new service
22 22
 
23 23
 Options:
24
-      --constraint list                    Placement constraints (default [])
25
-      --container-label list               Container labels (default [])
26
-  -d, --detach                             Exit immediately instead of waiting for the service to converge
27
-                                           (default true)
28
-      --dns list                           Set custom DNS servers (default [])
29
-      --dns-option list                    Set DNS options (default [])
30
-      --dns-search list                    Set custom DNS search domains (default [])
31
-      --endpoint-mode string               Endpoint mode ("vip"|"dnsrr") (default "vip")
32
-  -e, --env list                           Set environment variables (default [])
33
-      --env-file list                      Read in a file of environment variables (default [])
34
-      --group list                         Set one or more supplementary user groups for the container (default [])
24
+      --constraint list                    Placement constraints
25
+      --container-label list               Container labels
26
+  -d, --detach                             Exit immediately instead of waiting for the service to converge (default true)
27
+      --dns list                           Set custom DNS servers
28
+      --dns-option list                    Set DNS options
29
+      --dns-search list                    Set custom DNS search domains
30
+      --endpoint-mode string               Endpoint mode (vip or dnsrr) (default "vip")
31
+      --entrypoint command                 Overwrite the default ENTRYPOINT of the image
32
+  -e, --env list                           Set environment variables
33
+      --env-file list                      Read in a file of environment variables
34
+      --group list                         Set one or more supplementary user groups for the container
35 35
       --health-cmd string                  Command to run to check health
36 36
       --health-interval duration           Time between running the check (ns|us|ms|s|m|h)
37 37
       --health-retries int                 Consecutive failures needed to report unhealthy
38
+      --health-start-period duration       Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)
38 39
       --health-timeout duration            Maximum time to allow one check to run (ns|us|ms|s|m|h)
39
-      --health-start-period duration       Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s)
40 40
       --help                               Print usage
41
-      --host list                          Set one or more custom host-to-IP mappings (host:ip) (default [])
41
+      --host list                          Set one or more custom host-to-IP mappings (host:ip)
42 42
       --hostname string                    Container hostname
43
-  -l, --label list                         Service labels (default [])
44
-      --limit-cpu decimal                  Limit CPUs (default 0.000)
43
+  -l, --label list                         Service labels
44
+      --limit-cpu decimal                  Limit CPUs
45 45
       --limit-memory bytes                 Limit Memory
46 46
       --log-driver string                  Logging driver for service
47
-      --log-opt list                       Logging driver options (default [])
47
+      --log-opt list                       Logging driver options
48 48
       --mode string                        Service mode (replicated or global) (default "replicated")
49 49
       --mount mount                        Attach a filesystem mount to the service
50 50
       --name string                        Service name
51
-      --network list                       Network attachments (default [])
51
+      --network list                       Network attachments
52 52
       --no-healthcheck                     Disable any container-specified HEALTHCHECK
53 53
       --placement-pref pref                Add a placement preference
54 54
   -p, --publish port                       Publish a port as a node port
55
+  -q, --quiet                              Suppress progress output
55 56
       --read-only                          Mount the container's root filesystem as read only
56 57
       --replicas uint                      Number of tasks
57
-      --reserve-cpu decimal                Reserve CPUs (default 0.000)
58
+      --reserve-cpu decimal                Reserve CPUs
58 59
       --reserve-memory bytes               Reserve Memory
59
-      --restart-condition string           Restart when condition is met ("none"|"on-failure"|"any")
60
-      --restart-delay duration             Delay between restart attempts (ns|us|ms|s|m|h)
60
+      --restart-condition string           Restart when condition is met ("none"|"on-failure"|"any") (default "any")
61
+      --restart-delay duration             Delay between restart attempts (ns|us|ms|s|m|h) (default 5s)
61 62
       --restart-max-attempts uint          Maximum number of restarts before giving up
62 63
       --restart-window duration            Window used to evaluate the restart policy (ns|us|ms|s|m|h)
63 64
       --rollback-delay duration            Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)
64 65
       --rollback-failure-action string     Action on rollback failure ("pause"|"continue") (default "pause")
65
-      --rollback-max-failure-ratio float   Failure rate to tolerate during a rollback
66
-      --rollback-monitor duration          Duration after each task rollback to monitor for failure
67
-                                           (ns|us|ms|s|m|h) (default 0s)
66
+      --rollback-max-failure-ratio float   Failure rate to tolerate during a rollback (default 0)
67
+      --rollback-monitor duration          Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h) (default 5s)
68 68
       --rollback-order string              Rollback order ("start-first"|"stop-first") (default "stop-first")
69
-      --rollback-parallelism uint          Maximum number of tasks rolled back simultaneously (0 to roll
70
-                                           back all at once) (default 1)
69
+      --rollback-parallelism uint          Maximum number of tasks rolled back simultaneously (0 to roll back all at once) (default 1)
71 70
       --secret secret                      Specify secrets to expose to the service
72
-      --stop-grace-period duration         Time to wait before force killing a container (ns|us|ms|s|m|h)
71
+      --stop-grace-period duration         Time to wait before force killing a container (ns|us|ms|s|m|h) (default 10s)
73 72
       --stop-signal string                 Signal to stop the container
74 73
   -t, --tty                                Allocate a pseudo-TTY
75 74
       --update-delay duration              Delay between updates (ns|us|ms|s|m|h) (default 0s)
76 75
       --update-failure-action string       Action on update failure ("pause"|"continue"|"rollback") (default "pause")
77
-      --update-max-failure-ratio float     Failure rate to tolerate during an update
78
-      --update-monitor duration            Duration after each task update to monitor for failure (ns|us|ms|s|m|h)
76
+      --update-max-failure-ratio float     Failure rate to tolerate during an update (default 0)
77
+      --update-monitor duration            Duration after each task update to monitor for failure (ns|us|ms|s|m|h) (default 5s)
79 78
       --update-order string                Update order ("start-first"|"stop-first") (default "stop-first")
80 79
       --update-parallelism uint            Maximum number of tasks updated simultaneously (0 to update all at once) (default 1)
81 80
   -u, --user string                        Username or UID (format: <name|uid>[:<group|gid>])
... ...
@@ -21,43 +21,43 @@ Usage:  docker service update [OPTIONS] SERVICE
21 21
 Update a service
22 22
 
23 23
 Options:
24
-      --args string                        Service command args
25
-      --constraint-add list                Add or update a placement constraint (default [])
26
-      --constraint-rm list                 Remove a constraint (default [])
27
-      --container-label-add list           Add or update a container label (default [])
28
-      --container-label-rm list            Remove a container label by its key (default [])
29
-  -d, --detach                             Exit immediately instead of waiting for the service to converge
30
-                                           (default true)
31
-      --dns-add list                       Add or update a custom DNS server (default [])
32
-      --dns-option-add list                Add or update a DNS option (default [])
33
-      --dns-option-rm list                 Remove a DNS option (default [])
34
-      --dns-rm list                        Remove a custom DNS server (default [])
35
-      --dns-search-add list                Add or update a custom DNS search domain (default [])
36
-      --dns-search-rm list                 Remove a DNS search domain (default [])
37
-      --endpoint-mode string               Endpoint mode ("vip"|"dnsrr") (default "vip")
38
-      --env-add list                       Add or update an environment variable (default [])
39
-      --env-rm list                        Remove an environment variable (default [])
24
+      --args command                       Service command args
25
+      --constraint-add list                Add or update a placement constraint
26
+      --constraint-rm list                 Remove a constraint
27
+      --container-label-add list           Add or update a container label
28
+      --container-label-rm list            Remove a container label by its key
29
+  -d, --detach                             Exit immediately instead of waiting for the service to converge (default true)
30
+      --dns-add list                       Add or update a custom DNS server
31
+      --dns-option-add list                Add or update a DNS option
32
+      --dns-option-rm list                 Remove a DNS option
33
+      --dns-rm list                        Remove a custom DNS server
34
+      --dns-search-add list                Add or update a custom DNS search domain
35
+      --dns-search-rm list                 Remove a DNS search domain
36
+      --endpoint-mode string               Endpoint mode (vip or dnsrr)
37
+      --entrypoint command                 Overwrite the default ENTRYPOINT of the image
38
+      --env-add list                       Add or update an environment variable
39
+      --env-rm list                        Remove an environment variable
40 40
       --force                              Force update even if no changes require it
41
-      --group-add list                     Add an additional supplementary user group to the container (default [])
42
-      --group-rm list                      Remove a previously added supplementary user group from the container (default [])
41
+      --group-add list                     Add an additional supplementary user group to the container
42
+      --group-rm list                      Remove a previously added supplementary user group from the container
43 43
       --health-cmd string                  Command to run to check health
44 44
       --health-interval duration           Time between running the check (ns|us|ms|s|m|h)
45 45
       --health-retries int                 Consecutive failures needed to report unhealthy
46
+      --health-start-period duration       Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)
46 47
       --health-timeout duration            Maximum time to allow one check to run (ns|us|ms|s|m|h)
47
-      --health-start-period duration       Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s)
48 48
       --help                               Print usage
49
-      --host-add list                      Add or update a custom host-to-IP mapping (host:ip) (default [])
50
-      --host-rm list                       Remove a custom host-to-IP mapping (host:ip) (default [])
49
+      --host-add list                      Add or update a custom host-to-IP mapping (host:ip)
50
+      --host-rm list                       Remove a custom host-to-IP mapping (host:ip)
51 51
       --hostname string                    Container hostname
52 52
       --image string                       Service image tag
53
-      --label-add list                     Add or update a service label (default [])
54
-      --label-rm list                      Remove a label by its key (default [])
55
-      --limit-cpu decimal                  Limit CPUs (default 0.000)
53
+      --label-add list                     Add or update a service label
54
+      --label-rm list                      Remove a label by its key
55
+      --limit-cpu decimal                  Limit CPUs
56 56
       --limit-memory bytes                 Limit Memory
57 57
       --log-driver string                  Logging driver for service
58
-      --log-opt list                       Logging driver options (default [])
58
+      --log-opt list                       Logging driver options
59 59
       --mount-add mount                    Add or update a mount on a service
60
-      --mount-rm list                      Remove a mount by its target path (default [])
60
+      --mount-rm list                      Remove a mount by its target path
61 61
       --network-add list                   Add a network
62 62
       --network-rm list                    Remove a network
63 63
       --no-healthcheck                     Disable any container-specified HEALTHCHECK
... ...
@@ -65,34 +65,33 @@ Options:
65 65
       --placement-pref-rm pref             Remove a placement preference
66 66
       --publish-add port                   Add or update a published port
67 67
       --publish-rm port                    Remove a published port by its target port
68
+  -q, --quiet                              Suppress progress output
68 69
       --read-only                          Mount the container's root filesystem as read only
69 70
       --replicas uint                      Number of tasks
70
-      --reserve-cpu decimal                Reserve CPUs (default 0.000)
71
+      --reserve-cpu decimal                Reserve CPUs
71 72
       --reserve-memory bytes               Reserve Memory
72 73
       --restart-condition string           Restart when condition is met ("none"|"on-failure"|"any")
73 74
       --restart-delay duration             Delay between restart attempts (ns|us|ms|s|m|h)
74 75
       --restart-max-attempts uint          Maximum number of restarts before giving up
75 76
       --restart-window duration            Window used to evaluate the restart policy (ns|us|ms|s|m|h)
76 77
       --rollback                           Rollback to previous specification
77
-      --rollback-delay duration            Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)
78
-      --rollback-failure-action string     Action on rollback failure ("pause"|"continue") (default "pause")
78
+      --rollback-delay duration            Delay between task rollbacks (ns|us|ms|s|m|h)
79
+      --rollback-failure-action string     Action on rollback failure ("pause"|"continue")
79 80
       --rollback-max-failure-ratio float   Failure rate to tolerate during a rollback
80
-      --rollback-monitor duration          Duration after each task rollback to monitor for failure
81
-                                           (ns|us|ms|s|m|h) (default 0s)
81
+      --rollback-monitor duration          Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)
82 82
       --rollback-order string              Rollback order ("start-first"|"stop-first") (default "stop-first")
83
-      --rollback-parallelism uint          Maximum number of tasks rolled back simultaneously (0 to roll
84
-                                           back all at once) (default 1)
83
+      --rollback-parallelism uint          Maximum number of tasks rolled back simultaneously (0 to roll back all at once)
85 84
       --secret-add secret                  Add or update a secret on a service
86
-      --secret-rm list                     Remove a secret (default [])
85
+      --secret-rm list                     Remove a secret
87 86
       --stop-grace-period duration         Time to wait before force killing a container (ns|us|ms|s|m|h)
88 87
       --stop-signal string                 Signal to stop the container
89 88
   -t, --tty                                Allocate a pseudo-TTY
90
-      --update-delay duration              Delay between updates (ns|us|ms|s|m|h) (default 0s)
91
-      --update-failure-action string       Action on update failure ("pause"|"continue"|"rollback") (default "pause")
89
+      --update-delay duration              Delay between updates (ns|us|ms|s|m|h)
90
+      --update-failure-action string       Action on update failure ("pause"|"continue"|"rollback")
92 91
       --update-max-failure-ratio float     Failure rate to tolerate during an update
93
-      --update-monitor duration            Duration after each task update to monitor for failure (ns|us|ms|s|m|h) 
94
-      --update-order string                Update order ("start-first"|"stop-first") (default "stop-first")
95
-      --update-parallelism uint            Maximum number of tasks updated simultaneously (0 to update all at once) (default 1)
92
+      --update-monitor duration            Duration after each task update to monitor for failure (ns|us|ms|s|m|h)
93
+      --update-order string                Update order ("start-first"|"stop-first")
94
+      --update-parallelism uint            Maximum number of tasks updated simultaneously (0 to update all at once)
96 95
   -u, --user string                        Username or UID (format: <name|uid>[:<group|gid>])
97 96
       --with-registry-auth                 Send registry authentication details to swarm agents
98 97
   -w, --workdir string                     Working directory inside the container
... ...
@@ -60,6 +60,16 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *check.C) {
60 60
 	id := d.CreateService(c, simpleTestService, setInstances(instances))
61 61
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
62 62
 
63
+	// insertDefaults inserts UpdateConfig when service is fetched by ID
64
+	_, out, err := d.SockRequest("GET", "/services/"+id+"?insertDefaults=true", nil)
65
+	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
66
+	c.Assert(string(out), checker.Contains, "UpdateConfig")
67
+
68
+	// insertDefaults inserts UpdateConfig when service is fetched by ID
69
+	_, out, err = d.SockRequest("GET", "/services/top?insertDefaults=true", nil)
70
+	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
71
+	c.Assert(string(out), checker.Contains, "UpdateConfig")
72
+
63 73
 	service := d.GetService(c, id)
64 74
 	instances = 5
65 75
 	d.UpdateService(c, service, setInstances(instances))
... ...
@@ -38,7 +38,10 @@ func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
38 38
 }
39 39
 
40 40
 func (opts *ListOpts) String() string {
41
-	return fmt.Sprintf("%v", []string((*opts.values)))
41
+	if len(*opts.values) == 0 {
42
+		return ""
43
+	}
44
+	return fmt.Sprintf("%v", *opts.values)
42 45
 }
43 46
 
44 47
 // Set validates if needed the input value and adds it to the
... ...
@@ -343,6 +346,9 @@ type NanoCPUs int64
343 343
 
344 344
 // String returns the string format of the number
345 345
 func (c *NanoCPUs) String() string {
346
+	if *c == 0 {
347
+		return ""
348
+	}
346 349
 	return big.NewRat(c.Value(), 1e9).FloatString(3)
347 350
 }
348 351
 
... ...
@@ -93,12 +93,12 @@ func TestListOptsWithValidator(t *testing.T) {
93 93
 	// Re-using logOptsvalidator (used by MapOpts)
94 94
 	o := NewListOpts(logOptsValidator)
95 95
 	o.Set("foo")
96
-	if o.String() != "[]" {
97
-		t.Errorf("%s != []", o.String())
96
+	if o.String() != "" {
97
+		t.Errorf(`%s != ""`, o.String())
98 98
 	}
99 99
 	o.Set("foo=bar")
100
-	if o.String() != "[]" {
101
-		t.Errorf("%s != []", o.String())
100
+	if o.String() != "" {
101
+		t.Errorf(`%s != ""`, o.String())
102 102
 	}
103 103
 	o.Set("max-file=2")
104 104
 	if o.Len() != 1 {
... ...
@@ -111,8 +111,8 @@ func TestListOptsWithValidator(t *testing.T) {
111 111
 		t.Error("o.Get(\"baz\") == true")
112 112
 	}
113 113
 	o.Delete("max-file=2")
114
-	if o.String() != "[]" {
115
-		t.Errorf("%s != []", o.String())
114
+	if o.String() != "" {
115
+		t.Errorf(`%s != ""`, o.String())
116 116
 	}
117 117
 }
118 118