diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d759578..d03aa07e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,10 +19,35 @@ jobs: options: --privileged --cgroupns=host steps: - uses: actions/checkout@v4 - - name: Install dependencies + - name: Download necessary .deb files from man-compose-test-data repository + shell: bash + run: | + BASE_URL="https://raw.githubusercontent.com/mokibit/podman-compose-test-data/tree/main/deb_files/podman-5.4.2" + + FILES=( + "podman_5.4.2+composetest-1_amd64.deb" + "podman-docker_5.4.2+composetest-1_amd64.deb" + "podman-remote_5.4.2+composetest-1_amd64.deb" + ) + for FILE in "${FILES[@]}"; do + URL="${BASE_URL}/${FILE}" + echo "Downloading: $URL" + curl -L -f -O "$URL" || { echo "Failed to download $FILE"; exit 1; } + done + - name: Install podman v5.4.2 from .deb packages + run: | + dpkg -i podman_5.4.2+composetest-1_amd64.deb + dpkg -i podman-docker_5.4.2+composetest-1_amd64.deb + dpkg -i podman-remote_5.4.2+composetest-1_amd64.deb + apt-get install -f -y + - name: Verify podman installation + run: | + podman --version + podman-remote --version + podman-docker version || echo "podman-docker not available" + - name: Install other test dependencies run: | set -e - apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y podman python -m pip install --upgrade pip pip install -r requirements.txt pip install -r test-requirements.txt diff --git a/newsfragments/add-start_interval-in-healthcheck.misc b/newsfragments/add-start_interval-in-healthcheck.misc new file mode 100644 index 00000000..eba6270e --- /dev/null +++ b/newsfragments/add-start_interval-in-healthcheck.misc @@ -0,0 +1,2 @@ +Add support for `start_interval` option. +Rename healthcheck flags to be consistent with Podman options. diff --git a/podman_compose.py b/podman_compose.py index b4b24d54..a155d4a6 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -1286,7 +1286,7 @@ async def container_to_args( if isinstance(healthcheck_test, str): # podman does not add shell to handle command with whitespace podman_args.extend([ - "--healthcheck-command", + "--health-cmd", json.dumps(["CMD-SHELL", healthcheck_test]), ]) elif is_list(healthcheck_test): @@ -1296,11 +1296,11 @@ async def container_to_args( if healthcheck_type == "NONE": podman_args.append("--no-healthcheck") elif healthcheck_type == "CMD": - podman_args.extend(["--healthcheck-command", json.dumps(healthcheck_test)]) + podman_args.extend(["--health-cmd", json.dumps(healthcheck_test)]) elif healthcheck_type == "CMD-SHELL": if len(healthcheck_test) != 1: raise ValueError("'CMD_SHELL' takes a single string after it") - podman_args.extend(["--healthcheck-command", json.dumps(healthcheck_test)]) + podman_args.extend(["--health-cmd", json.dumps(healthcheck_test)]) else: raise ValueError( f"unknown healthcheck test type [{healthcheck_type}],\ @@ -1309,17 +1309,19 @@ async def container_to_args( else: raise ValueError("'healthcheck.test' either a string or a list") - # interval, timeout and start_period are specified as durations. + # interval, timeout, start_period, and start_interval are specified as durations. if "interval" in healthcheck: - podman_args.extend(["--healthcheck-interval", healthcheck["interval"]]) + podman_args.extend(["--health-interval", healthcheck["interval"]]) if "timeout" in healthcheck: - podman_args.extend(["--healthcheck-timeout", healthcheck["timeout"]]) + podman_args.extend(["--health-timeout", healthcheck["timeout"]]) if "start_period" in healthcheck: - podman_args.extend(["--healthcheck-start-period", healthcheck["start_period"]]) + podman_args.extend(["--health-start-period", healthcheck["start_period"]]) + if "start_interval" in healthcheck: + podman_args.extend(["--health-startup-interval", healthcheck["start_interval"]]) # convert other parameters to string if "retries" in healthcheck: - podman_args.extend(["--healthcheck-retries", str(healthcheck["retries"])]) + podman_args.extend(["--health-retries", str(healthcheck["retries"])]) # handle podman extension if 'x-podman' in cnt: diff --git a/tests/integration/healthcheck/__init__.py b/tests/integration/healthcheck/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/integration/healthcheck/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/integration/healthcheck/docker-compose.yml b/tests/integration/healthcheck/docker-compose.yml new file mode 100644 index 00000000..5c12b3c3 --- /dev/null +++ b/tests/integration/healthcheck/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3" +services: + healthcheck: + image: nopush/podman-compose-test + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost || exit 1" ] + interval: 1m + timeout: 10s + retries: 3 + start_period: 10s + start_interval: 5s diff --git a/tests/integration/healthcheck/test_podman_compose_healthcheck.py b/tests/integration/healthcheck/test_podman_compose_healthcheck.py new file mode 100644 index 00000000..50aa3c49 --- /dev/null +++ b/tests/integration/healthcheck/test_podman_compose_healthcheck.py @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0 + +import json +import os +import unittest + +from tests.integration.test_utils import RunSubprocessMixin +from tests.integration.test_utils import podman_compose_path +from tests.integration.test_utils import test_path + + +def compose_yaml_path() -> str: + return os.path.join(os.path.join(test_path(), "healthcheck"), "docker-compose.yml") + + +class TestHealthecheck(unittest.TestCase, RunSubprocessMixin): + def test_healthcheck(self) -> None: + up_cmd = [ + "coverage", + "run", + podman_compose_path(), + "-f", + os.path.join(test_path(), "healthcheck", "docker-compose.yml"), + "up", + "-d", + ] + self.run_subprocess_assert_returncode(up_cmd) + + command_container_id = [ + "podman", + "ps", + "-a", + "--filter", + "label=io.podman.compose.project=healthcheck", + "--format", + '"{{.ID}}"', + ] + out, _ = self.run_subprocess_assert_returncode(command_container_id) + self.assertNotEqual(out, b"") + container_id = out.decode("utf-8").strip().replace('"', "") + + command_inspect = ["podman", "container", "inspect", container_id] + + out, _ = self.run_subprocess_assert_returncode(command_inspect) + out_string = out.decode("utf-8") + inspect = json.loads(out_string) + healthcheck_obj = inspect[0]["Config"]["Healthcheck"] + expected = { + "Test": ["CMD-SHELL", "curl -f http://localhost || exit 1"], + "StartPeriod": 10000000000, + "Interval": 60000000000, + "Timeout": 10000000000, + "Retries": 3, + } + self.assertEqual(healthcheck_obj, expected) + + # StartInterval is not available in the config object + create_obj = inspect[0]["Config"]["CreateCommand"] + self.assertIn("--health-startup-interval", create_obj) diff --git a/tests/unit/test_container_to_args.py b/tests/unit/test_container_to_args.py index 3d035ae4..91490cd3 100644 --- a/tests/unit/test_container_to_args.py +++ b/tests/unit/test_container_to_args.py @@ -889,7 +889,7 @@ async def test_healthcheck_string(self) -> None: "--name=project_name_service_name1", "-d", "--network=bridge:alias=service_name", - "--healthcheck-command", + "--health-cmd", '["CMD-SHELL", "cmd arg1 arg2"]', "busybox", ], @@ -909,7 +909,7 @@ async def test_healthcheck_cmd_args(self) -> None: "--name=project_name_service_name1", "-d", "--network=bridge:alias=service_name", - "--healthcheck-command", + "--health-cmd", '["cmd", "arg1", "arg2"]', "busybox", ], @@ -929,7 +929,7 @@ async def test_healthcheck_cmd_shell(self) -> None: "--name=project_name_service_name1", "-d", "--network=bridge:alias=service_name", - "--healthcheck-command", + "--health-cmd", '["cmd arg1 arg2"]', "busybox", ],