Skip to content

Commit b5a1b51

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: #20212 Fixes: https://issues.redhat.com/browse/RUN-2629 Signed-off-by: Jan Rodák <[email protected]>
1 parent 6169343 commit b5a1b51

File tree

10 files changed

+147
-20
lines changed

10 files changed

+147
-20
lines changed

cmd/podman/common/create.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package common
22

33
import (
4+
"errors"
5+
"time"
6+
47
"github.com/containers/common/pkg/auth"
58
"github.com/containers/common/pkg/completion"
69
commonFlag "github.com/containers/common/pkg/flag"
10+
"github.com/containers/image/v5/manifest"
711
"github.com/containers/podman/v5/cmd/podman/registry"
812
"github.com/containers/podman/v5/libpod/define"
913
"github.com/containers/podman/v5/pkg/domain/entities"
@@ -1053,3 +1057,43 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
10531057
)
10541058
_ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault)
10551059
}
1060+
1061+
func GetHealthCheckOverrideConfig(cmd *cobra.Command, vals *entities.ContainerCreateOptions) (*manifest.Schema2HealthConfig, error) {
1062+
healthcheck := manifest.Schema2HealthConfig{}
1063+
1064+
if cmd.Flags().Changed("health-interval") {
1065+
if vals.HealthInterval == "disable" {
1066+
vals.HealthInterval = "-1s"
1067+
}
1068+
hct, err := time.ParseDuration(vals.HealthInterval)
1069+
if err != nil {
1070+
return nil, err
1071+
}
1072+
healthcheck.Interval = hct
1073+
}
1074+
if cmd.Flags().Changed("health-retries") {
1075+
healthcheck.Retries = int(vals.HealthRetries)
1076+
}
1077+
if cmd.Flags().Changed("health-timeout") {
1078+
hct, err := time.ParseDuration(vals.HealthTimeout)
1079+
if err != nil {
1080+
return nil, err
1081+
}
1082+
if hct < time.Duration(1) {
1083+
return nil, errors.New("healthcheck-timeout must be at least 1 second")
1084+
}
1085+
healthcheck.Timeout = hct
1086+
}
1087+
if cmd.Flags().Changed("health-start-period") {
1088+
hct, err := time.ParseDuration(vals.HealthStartPeriod)
1089+
if err != nil {
1090+
return nil, err
1091+
}
1092+
if hct < time.Duration(0) {
1093+
return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
1094+
}
1095+
healthcheck.StartPeriod = hct
1096+
}
1097+
1098+
return &healthcheck, nil
1099+
}

cmd/podman/containers/create.go

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

177+
if s.HealthConfig == nil {
178+
s.HealthConfig, err = common.GetHealthCheckOverrideConfig(cmd, &cliVals)
179+
if err != nil {
180+
return err
181+
}
182+
}
183+
177184
report, err := registry.ContainerEngine().ContainerCreate(registry.Context(), s)
178185
if err != nil {
179186
// if pod was created as part of run

cmd/podman/containers/run.go

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

228+
if s.HealthConfig == nil {
229+
s.HealthConfig, err = common.GetHealthCheckOverrideConfig(cmd, &cliVals)
230+
if err != nil {
231+
return err
232+
}
233+
}
234+
228235
report, err := registry.ContainerEngine().ContainerRun(registry.Context(), runOpts)
229236
// report.ExitCode is set by ContainerRun even it returns an error
230237
if report != nil {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ to be applied. A value of **none** disables existing healthchecks.
1010

1111
Multiple options can be passed in the form of a JSON array; otherwise, the command is interpreted
1212
as an argument to **/bin/sh -c**.
13+
14+
Note: The default values are used even if healthcheck is configured in the image.

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 will overwrite related 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 will overwrite related 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 will overwrite related healthcheck configuration from the image.

pkg/specgen/generate/container.go

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/containers/common/libimage"
1616
"github.com/containers/common/pkg/config"
17+
"github.com/containers/image/v5/manifest"
1718
"github.com/containers/podman/v5/libpod"
1819
"github.com/containers/podman/v5/libpod/define"
1920
ann "github.com/containers/podman/v5/pkg/annotations"
@@ -61,6 +62,66 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen
6162
return image, resolvedName, inspectData, err
6263
}
6364

65+
func applyHealthCheckOverrides(s *specgen.SpecGenerator, healthCheckFromImage *manifest.Schema2HealthConfig) error {
66+
overrideHealthCheckConfig := s.HealthConfig
67+
s.HealthConfig = healthCheckFromImage
68+
69+
if s.HealthConfig == nil {
70+
return nil
71+
}
72+
73+
if overrideHealthCheckConfig != nil {
74+
if overrideHealthCheckConfig.Interval != 0 {
75+
s.HealthConfig.Interval = overrideHealthCheckConfig.Interval
76+
}
77+
if overrideHealthCheckConfig.Retries != 0 {
78+
s.HealthConfig.Retries = overrideHealthCheckConfig.Retries
79+
}
80+
if overrideHealthCheckConfig.Timeout != 0 {
81+
s.HealthConfig.Timeout = overrideHealthCheckConfig.Timeout
82+
}
83+
if overrideHealthCheckConfig.StartPeriod != 0 {
84+
s.HealthConfig.StartPeriod = overrideHealthCheckConfig.StartPeriod
85+
}
86+
}
87+
88+
disableInterval := false
89+
if s.HealthConfig.Interval < 0 {
90+
s.HealthConfig.Interval = 0
91+
disableInterval = true
92+
}
93+
94+
// NOTE: Zero means inherit.
95+
if s.HealthConfig.Timeout == 0 {
96+
hct, err := time.ParseDuration(define.DefaultHealthCheckTimeout)
97+
if err != nil {
98+
return err
99+
}
100+
s.HealthConfig.Timeout = hct
101+
}
102+
if s.HealthConfig.Interval == 0 && !disableInterval {
103+
hct, err := time.ParseDuration(define.DefaultHealthCheckInterval)
104+
if err != nil {
105+
return err
106+
}
107+
s.HealthConfig.Interval = hct
108+
}
109+
110+
if s.HealthConfig.Retries == 0 {
111+
s.HealthConfig.Retries = int(define.DefaultHealthCheckRetries)
112+
}
113+
114+
if s.HealthConfig.StartPeriod == 0 {
115+
hct, err := time.ParseDuration(define.DefaultHealthCheckStartPeriod)
116+
if err != nil {
117+
return err
118+
}
119+
s.HealthConfig.StartPeriod = hct
120+
}
121+
122+
return nil
123+
}
124+
64125
// Fill any missing parts of the spec generator (e.g. from the image).
65126
// Returns a set of warnings or any fatal error that occurred.
66127
func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) ([]string, error) {
@@ -70,25 +131,9 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
70131
return nil, err
71132
}
72133
if inspectData != nil {
73-
if s.HealthConfig == nil {
74-
// NOTE: the health check is only set for Docker images
75-
// but inspect will take care of it.
76-
s.HealthConfig = inspectData.HealthCheck
77-
if s.HealthConfig != nil {
78-
if s.HealthConfig.Timeout == 0 {
79-
hct, err := time.ParseDuration(define.DefaultHealthCheckTimeout)
80-
if err != nil {
81-
return nil, err
82-
}
83-
s.HealthConfig.Timeout = hct
84-
}
85-
if s.HealthConfig.Interval == 0 {
86-
hct, err := time.ParseDuration(define.DefaultHealthCheckInterval)
87-
if err != nil {
88-
return nil, err
89-
}
90-
s.HealthConfig.Interval = hct
91-
}
134+
if s.HealthConfig == nil || len(s.HealthConfig.Test) == 0 {
135+
if err := applyHealthCheckOverrides(s, inspectData.HealthCheck); err != nil {
136+
return nil, err
92137
}
93138
}
94139

test/e2e/healthcheck_run_test.go

Lines changed: 15 additions & 1 deletion
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()
@@ -70,7 +84,7 @@ var _ = Describe("Podman healthcheck run", func() {
7084
hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Healthcheck}}", "hc"})
7185
hc.WaitWithDefaultTimeout()
7286
Expect(hc).Should(ExitCleanly())
73-
Expect(hc.OutputToString()).To(Equal("{[CMD-SHELL curl -f http://localhost/ || exit 1] 0s 0s 5m0s 3s 0}"))
87+
Expect(hc.OutputToString()).To(Equal("{[CMD-SHELL curl -f http://localhost/ || exit 1] 0s 0s 5m0s 3s 3}"))
7488
})
7589

7690
It("podman disable healthcheck with --health-cmd=none on valid container", func() {

0 commit comments

Comments
 (0)