Compat API: Fix healthcheck status and healthcheck config

Fixes:

- Do not show healthcheck status if not available or if container
  status is "created" (Docker behaviour)
- Show healthcheck configuration if present (Config.Healthcheck)

Tests:

- Ensure State.Health is not present if container status is "created"
- Ensure Config.Healthcheck is present and values correct
- Ensure State.Health is present if container started

Signed-off-by: Milivoje Legenovic <m.legenovic@gmail.com>
This commit is contained in:
Milivoje Legenovic
2021-08-03 13:48:07 +02:00
parent e93661f5e7
commit a210a22920
2 changed files with 56 additions and 21 deletions

View File

@ -403,22 +403,24 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
state.Status = define.ContainerStateCreated.String() state.Status = define.ContainerStateCreated.String()
} }
state.Health = &types.Health{ if l.HasHealthCheck() && state.Status != "created" {
Status: inspect.State.Healthcheck.Status, state.Health = &types.Health{
FailingStreak: inspect.State.Healthcheck.FailingStreak, Status: inspect.State.Healthcheck.Status,
} FailingStreak: inspect.State.Healthcheck.FailingStreak,
}
log := inspect.State.Healthcheck.Log log := inspect.State.Healthcheck.Log
for _, item := range log { for _, item := range log {
res := &types.HealthcheckResult{} res := &types.HealthcheckResult{}
s, _ := time.Parse(time.RFC3339Nano, item.Start) s, _ := time.Parse(time.RFC3339Nano, item.Start)
e, _ := time.Parse(time.RFC3339Nano, item.End) e, _ := time.Parse(time.RFC3339Nano, item.End)
res.Start = s res.Start = s
res.End = e res.End = e
res.ExitCode = item.ExitCode res.ExitCode = item.ExitCode
res.Output = item.Output res.Output = item.Output
state.Health.Log = append(state.Health.Log, res) state.Health.Log = append(state.Health.Log, res)
}
} }
formatCapabilities(inspect.HostConfig.CapDrop) formatCapabilities(inspect.HostConfig.CapDrop)
@ -495,6 +497,17 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
exposedPorts[exposedPort] = struct{}{} exposedPorts[exposedPort] = struct{}{}
} }
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,
}
}
config := container.Config{ config := container.Config{
Hostname: l.Hostname(), Hostname: l.Hostname(),
Domainname: inspect.Config.DomainName, Domainname: inspect.Config.DomainName,
@ -508,7 +521,7 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
StdinOnce: inspect.Config.StdinOnce, StdinOnce: inspect.Config.StdinOnce,
Env: inspect.Config.Env, Env: inspect.Config.Env,
Cmd: l.Command(), Cmd: l.Command(),
Healthcheck: nil, Healthcheck: healthcheck,
ArgsEscaped: false, ArgsEscaped: false,
Image: imageName, Image: imageName,
Volumes: nil, Volumes: nil,

View File

@ -36,26 +36,48 @@ class ContainerTestCase(APITestCase):
r = requests.post( r = requests.post(
self.podman_url + "/v1.40/containers/create?name=topcontainer", self.podman_url + "/v1.40/containers/create?name=topcontainer",
json={"Healthcheck": {"Test": ["CMD-SHELL", "exit 0"], "Interval":1000, "Timeout":1000, "Retries": 5}, "Cmd": ["top"], "Image": "alpine:latest"}, json={"Cmd": ["top"],
"Image": "alpine:latest",
"Healthcheck": {
"Test": ["CMD", "pidof", "top"],
"Interval": 5000000000,
"Timeout": 2000000000,
"Retries": 3,
"StartPeriod": 5000000000
}
},
) )
self.assertEqual(r.status_code, 201, r.text) self.assertEqual(r.status_code, 201, r.text)
payload = r.json() payload = r.json()
container_id = payload["Id"] container_id = payload["Id"]
self.assertIsNotNone(container_id) self.assertIsNotNone(container_id)
r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/json") r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json")
self.assertEqual(r.status_code, 200, r.text) self.assertEqual(r.status_code, 200, r.text)
self.assertId(r.content) self.assertId(r.content)
out = r.json() out = r.json()
state = out["State"]["Health"] self.assertIsNone(out["State"].get("Health"))
self.assertIsInstance(state, dict) self.assertListEqual(["CMD", "pidof", "top"], out["Config"]["Healthcheck"]["Test"])
self.assertEqual(5000000000, out["Config"]["Healthcheck"]["Interval"])
self.assertEqual(2000000000, out["Config"]["Healthcheck"]["Timeout"])
self.assertEqual(3, out["Config"]["Healthcheck"]["Retries"])
self.assertEqual(5000000000, out["Config"]["Healthcheck"]["StartPeriod"])
r = requests.get(self.uri(f"/containers/{payload['Id']}/json")) r = requests.get(self.uri(f"/containers/{container_id}/json"))
self.assertEqual(r.status_code, 200, r.text) self.assertEqual(r.status_code, 200, r.text)
self.assertId(r.content) self.assertId(r.content)
out = r.json() out = r.json()
hc = out["Config"]["Healthcheck"]["Test"] hc = out["Config"]["Healthcheck"]["Test"]
self.assertListEqual(["CMD-SHELL", "exit 0"], hc) self.assertListEqual(["CMD", "pidof", "top"], hc)
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start")
self.assertEqual(r.status_code, 204, r.text)
r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json")
self.assertEqual(r.status_code, 200, r.text)
out = r.json()
state = out["State"]["Health"]
self.assertIsInstance(state, dict)
def test_stats(self): def test_stats(self):
r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false"))) r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false")))