|
8 | 8 | "strings" |
9 | 9 | "time" |
10 | 10 |
|
| 11 | + "github.com/docker/docker/api/types/container" |
11 | 12 | mounttypes "github.com/docker/docker/api/types/mount" |
12 | 13 | "github.com/docker/docker/api/types/swarm" |
13 | 14 | "github.com/docker/docker/opts" |
@@ -68,6 +69,25 @@ func (c *nanoCPUs) Value() int64 { |
68 | 69 | return int64(*c) |
69 | 70 | } |
70 | 71 |
|
| 72 | +// PositiveDurationOpt is an option type for time.Duration that uses a pointer. |
| 73 | +// It bahave similarly to DurationOpt but only allows positive duration values. |
| 74 | +type PositiveDurationOpt struct { |
| 75 | + DurationOpt |
| 76 | +} |
| 77 | + |
| 78 | +// Set a new value on the option. Setting a negative duration value will cause |
| 79 | +// an error to be returned. |
| 80 | +func (d *PositiveDurationOpt) Set(s string) error { |
| 81 | + err := d.DurationOpt.Set(s) |
| 82 | + if err != nil { |
| 83 | + return err |
| 84 | + } |
| 85 | + if *d.DurationOpt.value < 0 { |
| 86 | + return fmt.Errorf("duration cannot be negative") |
| 87 | + } |
| 88 | + return nil |
| 89 | +} |
| 90 | + |
71 | 91 | // DurationOpt is an option type for time.Duration that uses a pointer. This |
72 | 92 | // allows us to get nil values outside, instead of defaulting to 0 |
73 | 93 | type DurationOpt struct { |
@@ -377,6 +397,47 @@ func (ldo *logDriverOptions) toLogDriver() *swarm.Driver { |
377 | 397 | } |
378 | 398 | } |
379 | 399 |
|
| 400 | +type healthCheckOptions struct { |
| 401 | + cmd string |
| 402 | + interval PositiveDurationOpt |
| 403 | + timeout PositiveDurationOpt |
| 404 | + retries int |
| 405 | + noHealthcheck bool |
| 406 | +} |
| 407 | + |
| 408 | +func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error) { |
| 409 | + var healthConfig *container.HealthConfig |
| 410 | + haveHealthSettings := opts.cmd != "" || |
| 411 | + opts.interval.Value() != nil || |
| 412 | + opts.timeout.Value() != nil || |
| 413 | + opts.retries != 0 |
| 414 | + if opts.noHealthcheck { |
| 415 | + if haveHealthSettings { |
| 416 | + return nil, fmt.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck) |
| 417 | + } |
| 418 | + healthConfig = &container.HealthConfig{Test: []string{"NONE"}} |
| 419 | + } else if haveHealthSettings { |
| 420 | + var test []string |
| 421 | + if opts.cmd != "" { |
| 422 | + test = []string{"CMD-SHELL", opts.cmd} |
| 423 | + } |
| 424 | + var interval, timeout time.Duration |
| 425 | + if ptr := opts.interval.Value(); ptr != nil { |
| 426 | + interval = *ptr |
| 427 | + } |
| 428 | + if ptr := opts.timeout.Value(); ptr != nil { |
| 429 | + timeout = *ptr |
| 430 | + } |
| 431 | + healthConfig = &container.HealthConfig{ |
| 432 | + Test: test, |
| 433 | + Interval: interval, |
| 434 | + Timeout: timeout, |
| 435 | + Retries: opts.retries, |
| 436 | + } |
| 437 | + } |
| 438 | + return healthConfig, nil |
| 439 | +} |
| 440 | + |
380 | 441 | // ValidatePort validates a string is in the expected format for a port definition |
381 | 442 | func ValidatePort(value string) (string, error) { |
382 | 443 | portMappings, err := nat.ParsePortSpec(value) |
@@ -416,6 +477,8 @@ type serviceOptions struct { |
416 | 477 | registryAuth bool |
417 | 478 |
|
418 | 479 | logDriver logDriverOptions |
| 480 | + |
| 481 | + healthcheck healthCheckOptions |
419 | 482 | } |
420 | 483 |
|
421 | 484 | func newServiceOptions() *serviceOptions { |
@@ -490,6 +553,12 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) { |
490 | 553 | EndpointSpec: opts.endpoint.ToEndpointSpec(), |
491 | 554 | } |
492 | 555 |
|
| 556 | + healthConfig, err := opts.healthcheck.toHealthConfig() |
| 557 | + if err != nil { |
| 558 | + return service, err |
| 559 | + } |
| 560 | + service.TaskTemplate.ContainerSpec.Healthcheck = healthConfig |
| 561 | + |
493 | 562 | switch opts.mode { |
494 | 563 | case "global": |
495 | 564 | if opts.replicas.Value() != nil { |
@@ -541,6 +610,12 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) { |
541 | 610 |
|
542 | 611 | flags.StringVar(&opts.logDriver.name, flagLogDriver, "", "Logging driver for service") |
543 | 612 | flags.Var(&opts.logDriver.opts, flagLogOpt, "Logging driver options") |
| 613 | + |
| 614 | + flags.StringVar(&opts.healthcheck.cmd, flagHealthCmd, "", "Command to run to check health") |
| 615 | + flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check") |
| 616 | + flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run") |
| 617 | + flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy") |
| 618 | + flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK") |
544 | 619 | } |
545 | 620 |
|
546 | 621 | const ( |
@@ -589,4 +664,9 @@ const ( |
589 | 664 | flagRegistryAuth = "with-registry-auth" |
590 | 665 | flagLogDriver = "log-driver" |
591 | 666 | flagLogOpt = "log-opt" |
| 667 | + flagHealthCmd = "health-cmd" |
| 668 | + flagHealthInterval = "health-interval" |
| 669 | + flagHealthRetries = "health-retries" |
| 670 | + flagHealthTimeout = "health-timeout" |
| 671 | + flagNoHealthcheck = "no-healthcheck" |
592 | 672 | ) |
0 commit comments