diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 78249974d8..bb6d80e71a 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -634,11 +634,12 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*handlers.LegacyImageI var healthcheck *container.HealthConfig if inspect.Config.Healthcheck != nil { healthcheck = &container.HealthConfig{ - Test: inspect.Config.Healthcheck.Test, - Interval: inspect.Config.Healthcheck.Interval, - Timeout: inspect.Config.Healthcheck.Timeout, - StartPeriod: inspect.Config.Healthcheck.StartPeriod, - Retries: inspect.Config.Healthcheck.Retries, + Test: inspect.Config.Healthcheck.Test, + Interval: inspect.Config.Healthcheck.Interval, + Timeout: inspect.Config.Healthcheck.Timeout, + StartPeriod: inspect.Config.Healthcheck.StartPeriod, + StartInterval: inspect.Config.Healthcheck.StartInterval, + Retries: inspect.Config.Healthcheck.Retries, } } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index a82951037a..5692be1d22 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -18,6 +18,7 @@ import ( "go.podman.io/common/libimage" "go.podman.io/common/libnetwork/types" "go.podman.io/common/pkg/config" + "go.podman.io/image/v5/manifest" "go.podman.io/podman/v6/libpod" "go.podman.io/podman/v6/libpod/define" "go.podman.io/podman/v6/pkg/api/handlers" @@ -116,6 +117,20 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("fill out specgen: %w", err)) return } + + // empty test command means inherit the image healthcheck, but we need to preserve the other health config fields if provided + if hc := body.Config.Healthcheck; hc != nil && len(hc.Test) == 0 && sg.HealthConfig == nil { + if hc.Interval != 0 || hc.Timeout != 0 || hc.Retries != 0 || hc.StartPeriod != 0 || hc.StartInterval != 0 { + sg.HealthConfig = &manifest.Schema2HealthConfig{ + Interval: hc.Interval, + Timeout: hc.Timeout, + Retries: hc.Retries, + StartPeriod: hc.StartPeriod, + StartInterval: hc.StartInterval, + } + } + } + // moby always create the working directory localTrue := true sg.CreateWorkingDir = &localTrue @@ -594,7 +609,7 @@ func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.C if cc.HostConfig.OomKillDisable != nil { cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable } - if cc.Config.Healthcheck != nil { + if cc.Config.Healthcheck != nil && len(cc.Config.Healthcheck.Test) > 0 { // Encode healthcheck test as JSON to preserve arguments with spaces. // MakeHealthCheckFromCli will unmarshal this back to the original array. cmdJSON, err := json.Marshal(cc.Config.Healthcheck.Test) diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index ed1ef7670e..dfd7749b5e 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -84,6 +84,9 @@ func applyHealthCheckOverrides(s *specgen.SpecGenerator, healthCheckFromImage *m if overrideHealthCheckConfig.StartPeriod != 0 { s.HealthConfig.StartPeriod = overrideHealthCheckConfig.StartPeriod } + if overrideHealthCheckConfig.StartInterval != 0 { + s.HealthConfig.StartInterval = overrideHealthCheckConfig.StartInterval + } } disableInterval := false diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 498d82cb58..3a1f386ac1 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -639,6 +639,59 @@ t GET containers/$cid/json 200 \ t DELETE containers/$cid?v=true 204 rm -rf $HEALTHCHECK_TMPD +# Test Compat Create with an empty healthcheck (compose "healthcheck: {}") inherits image health check +HEALTHCHECK_INHERIT_TMPD=$(mktemp -d podman-apiv2-test.healthcheck-inherit.XXXXXXXX) +cat >$HEALTHCHECK_INHERIT_TMPD/Dockerfile </dev/null + +# empty Test inherits the image healthcheck unchanged (all fields preserved) +t POST containers/create Image=healthcheck_inherit Cmd='["top"]' Healthcheck='{"Test":[]}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .Config.Healthcheck.Test[0]="CMD-SHELL" \ + .Config.Healthcheck.Test[1]="true" \ + .Config.Healthcheck.Interval=99000000000 \ + .Config.Healthcheck.Timeout=98000000000 \ + .Config.Healthcheck.StartPeriod=97000000000 \ + .Config.Healthcheck.StartInterval=96000000000 \ + .Config.Healthcheck.Retries=95 +t DELETE containers/$cid?v=true 204 + +# empty Test with explicit fields inherits the command, overrides every field +t POST containers/create Image=healthcheck_inherit Cmd='["top"]' Healthcheck='{"Test":[],"Interval":10000000000,"Timeout":11000000000,"StartPeriod":12000000000,"StartInterval":13000000000,"Retries":14}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .Config.Healthcheck.Test[0]="CMD-SHELL" \ + .Config.Healthcheck.Test[1]="true" \ + .Config.Healthcheck.Interval=10000000000 \ + .Config.Healthcheck.Timeout=11000000000 \ + .Config.Healthcheck.StartPeriod=12000000000 \ + .Config.Healthcheck.StartInterval=13000000000 \ + .Config.Healthcheck.Retries=14 +t DELETE containers/$cid?v=true 204 + +# empty Test overrides explicitly provided fields, inherits the rest +t POST containers/create Image=healthcheck_inherit Cmd='["top"]' Healthcheck='{"Test":[],"Timeout":11000000000,"Retries":14}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .Config.Healthcheck.Test[0]="CMD-SHELL" \ + .Config.Healthcheck.Test[1]="true" \ + .Config.Healthcheck.Interval=99000000000 \ + .Config.Healthcheck.Timeout=11000000000 \ + .Config.Healthcheck.StartPeriod=97000000000 \ + .Config.Healthcheck.StartInterval=96000000000 \ + .Config.Healthcheck.Retries=14 +t DELETE containers/$cid?v=true 204 + +podman rmi -f healthcheck_inherit &>/dev/null +rm -rf $HEALTHCHECK_INHERIT_TMPD + # compat api: Test for mount options support # Sigh, JSON can't handle octal. 0755(octal) = 493(decimal) payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":493}}]}'