support healthcheck: {} inherits healthcheck from image

Fixes: #29467

Signed-off-by: James Balazs <j.c.balazs1@gmail.com>
This commit is contained in:
James Balazs 2026-08-12 04:23:26 +00:00
parent 0d43350b9c
commit 16a1d3fbe9
4 changed files with 78 additions and 6 deletions

View file

@ -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,
}
}

View file

@ -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)

View file

@ -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

View file

@ -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 <<EOF
FROM $IMAGE
HEALTHCHECK --interval=99s --timeout=98s --start-period=97s --start-interval=96s --retries=95 CMD true
EOF
podman build -t healthcheck_inherit $HEALTHCHECK_INHERIT_TMPD &>/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}}]}'