Skip to content

Commit 741c89b

Browse files
committed
Fix overwriting the Healthcheck configuration from the image
If the --health-cmd flag is not specified, other flags such as --health-interval, --health-timeout, --health-retries, and --health-start-period are ignored if the image contains a Healthcheck. This makes it impossible to modify these Healthcheck configuration when a container is created. Fixes: containers#20212 Fixes: https://issues.redhat.com/browse/RUN-2629 Signed-off-by: Jan Rodák <[email protected]>
1 parent ffcad3c commit 741c89b

File tree

10 files changed

+99
-0
lines changed

10 files changed

+99
-0
lines changed

cmd/podman/common/create.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package common
22

33
import (
4+
"fmt"
5+
46
"github.com/containers/common/pkg/auth"
57
"github.com/containers/common/pkg/completion"
68
commonFlag "github.com/containers/common/pkg/flag"
@@ -1053,3 +1055,21 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
10531055
)
10541056
_ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault)
10551057
}
1058+
1059+
func GetHealthCheckOverrideConfig(cmd *cobra.Command, vals *entities.ContainerCreateOptions) map[string]string {
1060+
out := make(map[string]string)
1061+
1062+
if cmd.Flags().Changed("health-interval") {
1063+
out["health-interval"] = vals.HealthInterval
1064+
}
1065+
if cmd.Flags().Changed("health-retries") {
1066+
out["health-retries"] = fmt.Sprintf("%d", vals.HealthRetries)
1067+
}
1068+
if cmd.Flags().Changed("health-timeout") {
1069+
out["health-timeout"] = vals.HealthTimeout
1070+
}
1071+
if cmd.Flags().Changed("health-start-period") {
1072+
out["health-start-period"] = vals.HealthStartPeriod
1073+
}
1074+
return out
1075+
}

cmd/podman/containers/create.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ func create(cmd *cobra.Command, args []string) error {
174174
}
175175
}
176176

177+
if s.HealthConfig == nil {
178+
s.HealthImageOverride = common.GetHealthCheckOverrideConfig(cmd, &cliVals)
179+
}
180+
177181
report, err := registry.ContainerEngine().ContainerCreate(registry.Context(), s)
178182
if err != nil {
179183
// if pod was created as part of run

cmd/podman/containers/run.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ func run(cmd *cobra.Command, args []string) error {
225225
return err
226226
}
227227

228+
if s.HealthConfig == nil {
229+
s.HealthImageOverride = common.GetHealthCheckOverrideConfig(cmd, &cliVals)
230+
}
231+
228232
report, err := registry.ContainerEngine().ContainerRun(registry.Context(), runOpts)
229233
// report.ExitCode is set by ContainerRun even it returns an error
230234
if report != nil {

docs/source/markdown/options/health-interval.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
#### **--health-interval**=*interval*
66

77
Set an interval for the healthchecks. An _interval_ of **disable** results in no automatic timer setup. The default is **30s**.
8+
9+
Note: This parameter can overwrite the healthcheck configuration from the image.

docs/source/markdown/options/health-retries.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
#### **--health-retries**=*retries*
66

77
The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is **3**.
8+
9+
Note: This parameter can overwrite the healthcheck configuration from the image.

docs/source/markdown/options/health-start-period.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ the container's health state will be updated to `healthy`. However, if the healt
1212
stay as `starting` until either the health check is successful or until the `--health-start-period` time is over. If the
1313
health check command fails after the `--health-start-period` time is over, the health state will be updated to `unhealthy`.
1414
The health check command is executed periodically based on the value of `--health-interval`.
15+
16+
Note: This parameter can overwrite the healthcheck configuration from the image.

docs/source/markdown/options/health-timeout.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ value can be expressed in a time format such as **1m22s**. The default value is
1010
Note: A timeout marks the healthcheck as failed but does not terminate the running process.
1111
This ensures that a slow but eventually successful healthcheck does not disrupt the container
1212
but is still accounted for in the health status.
13+
14+
Note: This parameter can overwrite the healthcheck configuration from the image.

pkg/specgen/generate/container.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,52 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen
6161
return image, resolvedName, inspectData, err
6262
}
6363

64+
func applyHealthCheckOverrides(s *specgen.SpecGenerator) error {
65+
for k, v := range s.HealthImageOverride {
66+
switch k {
67+
case "health-interval":
68+
if v == "disable" {
69+
v = "0"
70+
}
71+
hct, err := time.ParseDuration(v)
72+
if err != nil {
73+
return err
74+
}
75+
s.HealthConfig.Interval = hct
76+
case "health-retries":
77+
i, err := strconv.Atoi(v)
78+
if err != nil {
79+
return err
80+
}
81+
if i < 1 {
82+
return errors.New("healthcheck-retries must be greater than 0")
83+
}
84+
s.HealthConfig.Retries = i
85+
case "health-timeout":
86+
hct, err := time.ParseDuration(v)
87+
if err != nil {
88+
return err
89+
}
90+
if hct < time.Duration(1) {
91+
return errors.New("healthcheck-timeout must be at least 1 second")
92+
}
93+
s.HealthConfig.Timeout = hct
94+
case "health-start-period":
95+
hct, err := time.ParseDuration(v)
96+
if err != nil {
97+
return err
98+
}
99+
if hct < time.Duration(0) {
100+
return errors.New("healthcheck-start-period must be 0 seconds or greater")
101+
}
102+
s.HealthConfig.StartPeriod = hct
103+
default:
104+
return fmt.Errorf("unknown healthcheck override key %q", k)
105+
}
106+
}
107+
return nil
108+
}
109+
64110
// Fill any missing parts of the spec generator (e.g. from the image).
65111
// Returns a set of warnings or any fatal error that occurred.
66112
func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) ([]string, error) {
@@ -89,6 +135,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
89135
}
90136
s.HealthConfig.Interval = hct
91137
}
138+
applyHealthCheckOverrides(s)
92139
}
93140
}
94141

pkg/specgen/specgen.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,8 @@ type ContainerHealthCheckConfig struct {
615615
// ("0" value means an infinite log length).
616616
// TODO (6.0): In next major release convert it to pointer and use omitempty
617617
HealthMaxLogSize uint `json:"healthMaxLogSize"`
618+
// HealthImageOverride is a map of healthcheck flags that overrides healthcheck configuration from image.
619+
HealthImageOverride map[string]string `json:"HealthImageOverride,omitempty"`
618620
}
619621

620622
// SpecGenerator creates an OCI spec and Libpod configuration options to create

test/e2e/healthcheck_run_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ var _ = Describe("Podman healthcheck run", func() {
3232
Expect(hc).Should(ExitWithError(125, "has no defined healthcheck"))
3333
})
3434

35+
It("podman run/create override image healthcheck configuration", func() {
36+
podmanTest.PodmanExitCleanly("run", "-dt", "--name", "hc", "--health-start-period", "10s", "--health-interval", "10s", "--health-timeout", "10s", "--health-retries", "2", HEALTHCHECK_IMAGE)
37+
hc := podmanTest.PodmanExitCleanly("container", "inspect", "--format", "{{.Config.Healthcheck.StartPeriod}}--{{.Config.Healthcheck.Interval}}--{{.Config.Healthcheck.Timeout}}--{{.Config.Healthcheck.Retries}}", "hc")
38+
Expect(hc.OutputToString()).To(Equal("10s--10s--10s--2"))
39+
40+
podmanTest.PodmanExitCleanly("create", "-q", "--name", "hc1", "--health-start-period", "10s", "--health-interval", "10s", "--health-timeout", "10s", "--health-retries", "2", "quay.io/libpod/healthcheck:config-only", "ls")
41+
hc1 := podmanTest.PodmanExitCleanly("container", "inspect", "--format", "{{.Config.Healthcheck.StartPeriod}}--{{.Config.Healthcheck.Interval}}--{{.Config.Healthcheck.Timeout}}--{{.Config.Healthcheck.Retries}}", "hc1")
42+
Expect(hc1.OutputToString()).To(Equal("10s--10s--10s--2"))
43+
44+
podmanTest.PodmanExitCleanly("run", "-dt", "--name", "hc2", "--health-start-period", "10s", "--health-interval", "disable", "--health-timeout", "10s", "--health-retries", "2", HEALTHCHECK_IMAGE)
45+
hc2 := podmanTest.PodmanExitCleanly("container", "inspect", "--format", "{{.Config.Healthcheck.StartPeriod}}--{{.Config.Healthcheck.Interval}}--{{.Config.Healthcheck.Timeout}}--{{.Config.Healthcheck.Retries}}", "hc2")
46+
Expect(hc2.OutputToString()).To(Equal("10s--0s--10s--2"))
47+
})
48+
3549
It("podman disable healthcheck with --no-healthcheck must not show starting on status", func() {
3650
session := podmanTest.Podman([]string{"run", "-dt", "--no-healthcheck", "--name", "hc", HEALTHCHECK_IMAGE})
3751
session.WaitWithDefaultTimeout()

0 commit comments

Comments
 (0)