Added start period option to health check.

Signed-off-by: Elias Faxö <elias.faxo@gmail.com>
This commit is contained in:
Elias Faxö 2016-11-29 10:58:47 +01:00 committed by Elias Faxö
parent 738ac9e797
commit a58f798fdf
8 changed files with 68 additions and 33 deletions

View File

@ -113,6 +113,7 @@ type containerOptions struct {
healthCmd string healthCmd string
healthInterval time.Duration healthInterval time.Duration
healthTimeout time.Duration healthTimeout time.Duration
healthStartPeriod time.Duration
healthRetries int healthRetries int
runtime string runtime string
autoRemove bool autoRemove bool
@ -232,6 +233,8 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ns|us|ms|s|m|h) (default 0s)") flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ns|us|ms|s|m|h) (default 0s)")
flags.IntVar(&copts.healthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy") flags.IntVar(&copts.healthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy")
flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ns|us|ms|s|m|h) (default 0s)") flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ns|us|ms|s|m|h) (default 0s)")
flags.DurationVar(&copts.healthStartPeriod, "health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown (ns|us|ms|s|m|h) (default 0s)")
flags.SetAnnotation("health-start-period", "version", []string{"1.29"})
flags.BoolVar(&copts.noHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK") flags.BoolVar(&copts.noHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK")
// Resource management // Resource management
@ -464,6 +467,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
haveHealthSettings := copts.healthCmd != "" || haveHealthSettings := copts.healthCmd != "" ||
copts.healthInterval != 0 || copts.healthInterval != 0 ||
copts.healthTimeout != 0 || copts.healthTimeout != 0 ||
copts.healthStartPeriod != 0 ||
copts.healthRetries != 0 copts.healthRetries != 0
if copts.noHealthcheck { if copts.noHealthcheck {
if haveHealthSettings { if haveHealthSettings {
@ -486,12 +490,16 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
if copts.healthRetries < 0 { if copts.healthRetries < 0 {
return nil, errors.Errorf("--health-retries cannot be negative") return nil, errors.Errorf("--health-retries cannot be negative")
} }
if copts.healthStartPeriod < 0 {
return nil, fmt.Errorf("--health-start-period cannot be negative")
}
healthConfig = &container.HealthConfig{ healthConfig = &container.HealthConfig{
Test: probe, Test: probe,
Interval: copts.healthInterval, Interval: copts.healthInterval,
Timeout: copts.healthTimeout, Timeout: copts.healthTimeout,
Retries: copts.healthRetries, StartPeriod: copts.healthStartPeriod,
Retries: copts.healthRetries,
} }
} }

View File

@ -501,8 +501,8 @@ func TestParseHealth(t *testing.T) {
checkError("--no-healthcheck conflicts with --health-* options", checkError("--no-healthcheck conflicts with --health-* options",
"--no-healthcheck", "--health-cmd=/check.sh -q", "img", "cmd") "--no-healthcheck", "--health-cmd=/check.sh -q", "img", "cmd")
health = checkOk("--health-timeout=2s", "--health-retries=3", "--health-interval=4.5s", "img", "cmd") health = checkOk("--health-timeout=2s", "--health-retries=3", "--health-interval=4.5s", "--health-start-period=5s", "img", "cmd")
if health.Timeout != 2*time.Second || health.Retries != 3 || health.Interval != 4500*time.Millisecond { if health.Timeout != 2*time.Second || health.Retries != 3 || health.Interval != 4500*time.Millisecond || health.StartPeriod != 5*time.Second {
t.Fatalf("--health-*: got %#v", health) t.Fatalf("--health-*: got %#v", health)
} }
} }

View File

@ -282,6 +282,7 @@ type healthCheckOptions struct {
interval PositiveDurationOpt interval PositiveDurationOpt
timeout PositiveDurationOpt timeout PositiveDurationOpt
retries int retries int
startPeriod PositiveDurationOpt
noHealthcheck bool noHealthcheck bool
} }
@ -301,18 +302,22 @@ func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error
if opts.cmd != "" { if opts.cmd != "" {
test = []string{"CMD-SHELL", opts.cmd} test = []string{"CMD-SHELL", opts.cmd}
} }
var interval, timeout time.Duration var interval, timeout, startPeriod time.Duration
if ptr := opts.interval.Value(); ptr != nil { if ptr := opts.interval.Value(); ptr != nil {
interval = *ptr interval = *ptr
} }
if ptr := opts.timeout.Value(); ptr != nil { if ptr := opts.timeout.Value(); ptr != nil {
timeout = *ptr timeout = *ptr
} }
if ptr := opts.startPeriod.Value(); ptr != nil {
startPeriod = *ptr
}
healthConfig = &container.HealthConfig{ healthConfig = &container.HealthConfig{
Test: test, Test: test,
Interval: interval, Interval: interval,
Timeout: timeout, Timeout: timeout,
Retries: opts.retries, Retries: opts.retries,
StartPeriod: startPeriod,
} }
} }
return healthConfig, nil return healthConfig, nil
@ -555,6 +560,8 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
flags.SetAnnotation(flagHealthTimeout, "version", []string{"1.25"}) flags.SetAnnotation(flagHealthTimeout, "version", []string{"1.25"})
flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy") flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy")
flags.SetAnnotation(flagHealthRetries, "version", []string{"1.25"}) flags.SetAnnotation(flagHealthRetries, "version", []string{"1.25"})
flags.Var(&opts.healthcheck.startPeriod, flagHealthStartPeriod, "Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)")
flags.SetAnnotation(flagHealthStartPeriod, "version", []string{"1.29"})
flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK") flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
flags.SetAnnotation(flagNoHealthcheck, "version", []string{"1.25"}) flags.SetAnnotation(flagNoHealthcheck, "version", []string{"1.25"})
@ -644,6 +651,7 @@ const (
flagHealthInterval = "health-interval" flagHealthInterval = "health-interval"
flagHealthRetries = "health-retries" flagHealthRetries = "health-retries"
flagHealthTimeout = "health-timeout" flagHealthTimeout = "health-timeout"
flagHealthStartPeriod = "health-start-period"
flagNoHealthcheck = "no-healthcheck" flagNoHealthcheck = "no-healthcheck"
flagSecret = "secret" flagSecret = "secret"
flagSecretAdd = "secret-add" flagSecretAdd = "secret-add"

View File

@ -71,18 +71,20 @@ func TestUint64OptSetAndValue(t *testing.T) {
func TestHealthCheckOptionsToHealthConfig(t *testing.T) { func TestHealthCheckOptionsToHealthConfig(t *testing.T) {
dur := time.Second dur := time.Second
opt := healthCheckOptions{ opt := healthCheckOptions{
cmd: "curl", cmd: "curl",
interval: PositiveDurationOpt{DurationOpt{value: &dur}}, interval: PositiveDurationOpt{DurationOpt{value: &dur}},
timeout: PositiveDurationOpt{DurationOpt{value: &dur}}, timeout: PositiveDurationOpt{DurationOpt{value: &dur}},
retries: 10, startPeriod: PositiveDurationOpt{DurationOpt{value: &dur}},
retries: 10,
} }
config, err := opt.toHealthConfig() config, err := opt.toHealthConfig()
assert.NilError(t, err) assert.NilError(t, err)
assert.Equal(t, reflect.DeepEqual(config, &container.HealthConfig{ assert.Equal(t, reflect.DeepEqual(config, &container.HealthConfig{
Test: []string{"CMD-SHELL", "curl"}, Test: []string{"CMD-SHELL", "curl"},
Interval: time.Second, Interval: time.Second,
Timeout: time.Second, Timeout: time.Second,
Retries: 10, StartPeriod: time.Second,
Retries: 10,
}), true) }), true)
} }

View File

@ -897,7 +897,7 @@ func updateLogDriver(flags *pflag.FlagSet, taskTemplate *swarm.TaskSpec) error {
} }
func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) error { func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) error {
if !anyChanged(flags, flagNoHealthcheck, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) { if !anyChanged(flags, flagNoHealthcheck, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout, flagHealthStartPeriod) {
return nil return nil
} }
if containerSpec.Healthcheck == nil { if containerSpec.Healthcheck == nil {
@ -908,7 +908,7 @@ func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec)
return err return err
} }
if noHealthcheck { if noHealthcheck {
if !anyChanged(flags, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) { if !anyChanged(flags, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout, flagHealthStartPeriod) {
containerSpec.Healthcheck = &container.HealthConfig{ containerSpec.Healthcheck = &container.HealthConfig{
Test: []string{"NONE"}, Test: []string{"NONE"},
} }
@ -927,6 +927,10 @@ func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec)
val := *flags.Lookup(flagHealthTimeout).Value.(*PositiveDurationOpt).Value() val := *flags.Lookup(flagHealthTimeout).Value.(*PositiveDurationOpt).Value()
containerSpec.Healthcheck.Timeout = val containerSpec.Healthcheck.Timeout = val
} }
if flags.Changed(flagHealthStartPeriod) {
val := *flags.Lookup(flagHealthStartPeriod).Value.(*PositiveDurationOpt).Value()
containerSpec.Healthcheck.StartPeriod = val
}
if flags.Changed(flagHealthRetries) { if flags.Changed(flagHealthRetries) {
containerSpec.Healthcheck.Retries, _ = flags.GetInt(flagHealthRetries) containerSpec.Healthcheck.Retries, _ = flags.GetInt(flagHealthRetries)
} }

View File

@ -311,6 +311,11 @@ func TestUpdateHealthcheckTable(t *testing.T) {
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10}, initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}}, expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
}, },
{
flags: [][2]string{{"health-start-period", "1m"}},
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, StartPeriod: time.Minute},
},
{ {
flags: [][2]string{{"health-cmd", "cmd1"}, {"no-healthcheck", "true"}}, flags: [][2]string{{"health-cmd", "cmd1"}, {"no-healthcheck", "true"}},
err: "--no-healthcheck conflicts with --health-* options", err: "--no-healthcheck conflicts with --health-* options",

View File

@ -255,9 +255,9 @@ func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container
return nil, nil return nil, nil
} }
var ( var (
err error err error
timeout, interval time.Duration timeout, interval, startPeriod time.Duration
retries int retries int
) )
if healthcheck.Disable { if healthcheck.Disable {
if len(healthcheck.Test) != 0 { if len(healthcheck.Test) != 0 {
@ -280,14 +280,21 @@ func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container
return nil, err return nil, err
} }
} }
if healthcheck.StartPeriod != "" {
startPeriod, err = time.ParseDuration(healthcheck.StartPeriod)
if err != nil {
return nil, err
}
}
if healthcheck.Retries != nil { if healthcheck.Retries != nil {
retries = int(*healthcheck.Retries) retries = int(*healthcheck.Retries)
} }
return &container.HealthConfig{ return &container.HealthConfig{
Test: healthcheck.Test, Test: healthcheck.Test,
Timeout: timeout, Timeout: timeout,
Interval: interval, Interval: interval,
Retries: retries, Retries: retries,
StartPeriod: startPeriod,
}, nil }, nil
} }

View File

@ -163,11 +163,12 @@ type DeployConfig struct {
// HealthCheckConfig the healthcheck configuration for a service // HealthCheckConfig the healthcheck configuration for a service
type HealthCheckConfig struct { type HealthCheckConfig struct {
Test HealthCheckTest Test HealthCheckTest
Timeout string Timeout string
Interval string Interval string
Retries *uint64 Retries *uint64
Disable bool StartPeriod string
Disable bool
} }
// HealthCheckTest is the command run to test the health of a service // HealthCheckTest is the command run to test the health of a service