mirror of
https://github.com/containers/podman-compose.git
synced 2025-06-09 22:46:45 +02:00
Fix service_healthy condition enforcing
Skip dependency health check to avoid compose-up hang for podman prior to 4.6.0, which doesn't support --condition healthy. Signed-off-by: Justin Zhang <schnell18@gmail.com>
This commit is contained in:
parent
eba2ca2695
commit
d7762a54f0
1
newsfragments/1178.bugfix
Normal file
1
newsfragments/1178.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fixed up command hangs on Podman versions earlier than 4.6.0 (#1178)
|
1
newsfragments/1183.bugfix
Normal file
1
newsfragments/1183.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
- Fixed issue in up command where service_healthy conditions weren't being enforced (#1183)
|
@ -2815,6 +2815,18 @@ async def check_dep_conditions(compose: PodmanCompose, deps: set) -> None:
|
|||||||
deps_cd = []
|
deps_cd = []
|
||||||
for d in deps:
|
for d in deps:
|
||||||
if d.condition == condition:
|
if d.condition == condition:
|
||||||
|
if (
|
||||||
|
d.condition
|
||||||
|
in (ServiceDependencyCondition.HEALTHY, ServiceDependencyCondition.UNHEALTHY)
|
||||||
|
) and strverscmp_lt(compose.podman_version, "4.6.0"):
|
||||||
|
log.warning(
|
||||||
|
"Ignored %s condition check due to podman %s doesn't support %s!",
|
||||||
|
d.name,
|
||||||
|
compose.podman_version,
|
||||||
|
condition.value,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
deps_cd.extend(compose.container_names_by_service[d.name])
|
deps_cd.extend(compose.container_names_by_service[d.name])
|
||||||
|
|
||||||
if deps_cd:
|
if deps_cd:
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:8000/hosts"]
|
||||||
|
start_period: 10s # initialization time for containers that need time to bootstrap
|
||||||
|
interval: 10s # Time between health checks
|
||||||
|
timeout: 5s # Time to wait for a response
|
||||||
|
retries: 3 # Number of consecutive failures before marking as unhealthy
|
||||||
|
sleep:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||||
|
depends_on:
|
||||||
|
web:
|
||||||
|
condition: service_healthy
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
@ -2,7 +2,9 @@
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from tests.integration.test_utils import PodmanAwareRunSubprocessMixin
|
||||||
from tests.integration.test_utils import RunSubprocessMixin
|
from tests.integration.test_utils import RunSubprocessMixin
|
||||||
|
from tests.integration.test_utils import is_systemd_available
|
||||||
from tests.integration.test_utils import podman_compose_path
|
from tests.integration.test_utils import podman_compose_path
|
||||||
from tests.integration.test_utils import test_path
|
from tests.integration.test_utils import test_path
|
||||||
|
|
||||||
@ -183,3 +185,82 @@ class TestComposeConditionalDeps(unittest.TestCase, RunSubprocessMixin):
|
|||||||
compose_yaml_path(suffix),
|
compose_yaml_path(suffix),
|
||||||
"down",
|
"down",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class TestComposeConditionalDepsHealthy(unittest.TestCase, PodmanAwareRunSubprocessMixin):
|
||||||
|
def setUp(self):
|
||||||
|
self.podman_version = self.retrieve_podman_version()
|
||||||
|
|
||||||
|
def test_up_deps_healthy(self):
|
||||||
|
suffix = "-conditional-healthy"
|
||||||
|
try:
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_yaml_path(suffix),
|
||||||
|
"up",
|
||||||
|
"sleep",
|
||||||
|
"--detach",
|
||||||
|
])
|
||||||
|
|
||||||
|
# Since the command `podman wait --condition=healthy` is invalid prior to 4.6.0,
|
||||||
|
# we only validate healthy status for podman 4.6.0+, which won't be tested in the
|
||||||
|
# CI pipeline of the podman-compose project where podman 4.3.1 is employed.
|
||||||
|
podman_ver_major, podman_ver_minor, podman_ver_patch = self.podman_version
|
||||||
|
if podman_ver_major >= 4 and podman_ver_minor >= 6 and podman_ver_patch >= 0:
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
"podman",
|
||||||
|
"wait",
|
||||||
|
"--condition=running",
|
||||||
|
"deps_web_1",
|
||||||
|
"deps_sleep_1",
|
||||||
|
])
|
||||||
|
|
||||||
|
# check both web and sleep are running
|
||||||
|
output, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_yaml_path(),
|
||||||
|
"ps",
|
||||||
|
"--format",
|
||||||
|
"{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.StartedAt}}",
|
||||||
|
])
|
||||||
|
|
||||||
|
# extract container id of web
|
||||||
|
decoded_out = output.decode('utf-8')
|
||||||
|
lines = decoded_out.split("\n")
|
||||||
|
|
||||||
|
web_lines = [line for line in lines if "web" in line]
|
||||||
|
self.assertTrue(web_lines)
|
||||||
|
self.assertEqual(1, len(web_lines))
|
||||||
|
web_cnt_id, web_cnt_name, web_cnt_status, web_cnt_started = web_lines[0].split("\t")
|
||||||
|
self.assertNotEqual("", web_cnt_id)
|
||||||
|
self.assertEqual("deps_web_1", web_cnt_name)
|
||||||
|
|
||||||
|
sleep_lines = [line for line in lines if "sleep" in line]
|
||||||
|
self.assertTrue(sleep_lines)
|
||||||
|
self.assertEqual(1, len(sleep_lines))
|
||||||
|
sleep_cnt_id, sleep_cnt_name, _, sleep_cnt_started = sleep_lines[0].split("\t")
|
||||||
|
self.assertNotEqual("", sleep_cnt_id)
|
||||||
|
self.assertEqual("deps_sleep_1", sleep_cnt_name)
|
||||||
|
|
||||||
|
# When test case is executed inside container like github actions, the absence of
|
||||||
|
# systemd prevents health check from working properly, resulting in failure to
|
||||||
|
# transit to healthy state. As a result, we only assert the `healthy` state where
|
||||||
|
# systemd is functioning.
|
||||||
|
if (
|
||||||
|
is_systemd_available()
|
||||||
|
and podman_ver_major >= 4
|
||||||
|
and podman_ver_minor >= 6
|
||||||
|
and podman_ver_patch >= 0
|
||||||
|
):
|
||||||
|
self.assertIn("healthy", web_cnt_status)
|
||||||
|
self.assertGreaterEqual(int(sleep_cnt_started), int(web_cnt_started))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_yaml_path(),
|
||||||
|
"down",
|
||||||
|
])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -21,6 +22,14 @@ def podman_compose_path():
|
|||||||
return os.path.join(base_path(), "podman_compose.py")
|
return os.path.join(base_path(), "podman_compose.py")
|
||||||
|
|
||||||
|
|
||||||
|
def is_systemd_available():
|
||||||
|
try:
|
||||||
|
with open("/proc/1/comm", "r", encoding="utf-8") as fh:
|
||||||
|
return fh.read().strip() == "systemd"
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class RunSubprocessMixin:
|
class RunSubprocessMixin:
|
||||||
def is_debug_enabled(self):
|
def is_debug_enabled(self):
|
||||||
return "TESTS_DEBUG" in os.environ
|
return "TESTS_DEBUG" in os.environ
|
||||||
@ -52,3 +61,15 @@ class RunSubprocessMixin:
|
|||||||
f"stdout: {decoded_out}\nstderr: {decoded_err}\n",
|
f"stdout: {decoded_out}\nstderr: {decoded_err}\n",
|
||||||
)
|
)
|
||||||
return out, err
|
return out, err
|
||||||
|
|
||||||
|
|
||||||
|
class PodmanAwareRunSubprocessMixin(RunSubprocessMixin):
|
||||||
|
def retrieve_podman_version(self):
|
||||||
|
out, _ = self.run_subprocess_assert_returncode(["podman", "--version"])
|
||||||
|
matcher = re.match(r"\D*(\d+)\.(\d+)\.(\d+)", out.decode('utf-8'))
|
||||||
|
if matcher:
|
||||||
|
major = int(matcher.group(1))
|
||||||
|
minor = int(matcher.group(2))
|
||||||
|
patch = int(matcher.group(3))
|
||||||
|
return (major, minor, patch)
|
||||||
|
raise RuntimeError("Unable to retrieve podman version")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user