Improve default handling for "service create"
Sebastiaan van Stijn authored on 2017/04/11 20:06:53... | ... |
@@ -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 |
|