From 76b3055934ed7bbd0655d04fb247c95f137deaf1 Mon Sep 17 00:00:00 2001 From: Ben Krieger Date: Sun, 12 Jan 2025 22:49:46 -0500 Subject: [PATCH 1/2] Fix CMD healthchecks running with /bin/sh Signed-off-by: Ben Krieger --- newsfragments/fix-cmd-healtchecks.bugfix | 1 + podman_compose.py | 14 +++----------- 2 files changed, 4 insertions(+), 11 deletions(-) create mode 100644 newsfragments/fix-cmd-healtchecks.bugfix diff --git a/newsfragments/fix-cmd-healtchecks.bugfix b/newsfragments/fix-cmd-healtchecks.bugfix new file mode 100644 index 0000000..c3b53a5 --- /dev/null +++ b/newsfragments/fix-cmd-healtchecks.bugfix @@ -0,0 +1 @@ +Fixed support for CMD healthchecks to run using the given command directly and not using `/bin/sh -c`. diff --git a/podman_compose.py b/podman_compose.py index c2c88de..f0841d5 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -29,14 +29,8 @@ import urllib.parse from asyncio import Task from enum import Enum -try: - from shlex import quote as cmd_quote -except ImportError: - from pipes import quote as cmd_quote # pylint: disable=deprecated-module - # import fnmatch # fnmatch.fnmatchcase(env, "*_HOST") - import yaml from dotenv import dotenv_values @@ -1228,7 +1222,7 @@ async def container_to_args(compose, cnt, detached=True, no_deps=False): # podman does not add shell to handle command with whitespace podman_args.extend([ "--healthcheck-command", - "/bin/sh -c " + cmd_quote(healthcheck_test), + json.dumps(["CMD-SHELL", healthcheck_test]), ]) elif is_list(healthcheck_test): healthcheck_test = healthcheck_test.copy() @@ -1237,13 +1231,11 @@ async def container_to_args(compose, cnt, detached=True, no_deps=False): if healthcheck_type == "NONE": podman_args.append("--no-healthcheck") elif healthcheck_type == "CMD": - cmd_q = "' '".join([cmd_quote(i) for i in healthcheck_test]) - podman_args.extend(["--healthcheck-command", "/bin/sh -c " + cmd_q]) + podman_args.extend(["--healthcheck-command", json.dumps(healthcheck_test)]) elif healthcheck_type == "CMD-SHELL": if len(healthcheck_test) != 1: raise ValueError("'CMD_SHELL' takes a single string after it") - cmd_q = cmd_quote(healthcheck_test[0]) - podman_args.extend(["--healthcheck-command", "/bin/sh -c " + cmd_q]) + podman_args.extend(["--healthcheck-command", json.dumps(healthcheck_test)]) else: raise ValueError( f"unknown healthcheck test type [{healthcheck_type}],\ From a983129e88da161c7bc89930996c381e11d0724d Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Tue, 20 May 2025 13:09:37 +0300 Subject: [PATCH 2/2] tests/unit: Add unit tests for fixing CMD healthcheck Signed-off-by: Monika Kairaityte --- tests/unit/test_container_to_args.py | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/unit/test_container_to_args.py b/tests/unit/test_container_to_args.py index ee07b92..fc1085f 100644 --- a/tests/unit/test_container_to_args.py +++ b/tests/unit/test_container_to_args.py @@ -694,3 +694,73 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase): with self.assertRaises(ValueError): await container_to_args(c, cnt) + + async def test_heathcheck_string(self): + c = create_compose_mock() + cnt = get_minimal_container() + cnt["healthcheck"] = { + "test": "cmd arg1 arg2", + } + + args = await container_to_args(c, cnt) + self.assertEqual( + args, + [ + "--name=project_name_service_name1", + "-d", + "--network=bridge:alias=service_name", + "--healthcheck-command", + '["CMD-SHELL", "cmd arg1 arg2"]', + "busybox", + ], + ) + + async def test_heathcheck_cmd_args(self): + c = create_compose_mock() + cnt = get_minimal_container() + cnt["healthcheck"] = { + "test": ["CMD", "cmd", "arg1", "arg2"], + } + + args = await container_to_args(c, cnt) + self.assertEqual( + args, + [ + "--name=project_name_service_name1", + "-d", + "--network=bridge:alias=service_name", + "--healthcheck-command", + '["cmd", "arg1", "arg2"]', + "busybox", + ], + ) + + async def test_heathcheck_cmd_shell(self): + c = create_compose_mock() + cnt = get_minimal_container() + cnt["healthcheck"] = { + "test": ["CMD-SHELL", "cmd arg1 arg2"], + } + + args = await container_to_args(c, cnt) + self.assertEqual( + args, + [ + "--name=project_name_service_name1", + "-d", + "--network=bridge:alias=service_name", + "--healthcheck-command", + '["cmd arg1 arg2"]', + "busybox", + ], + ) + + async def test_heathcheck_cmd_shell_error(self): + c = create_compose_mock() + cnt = get_minimal_container() + cnt["healthcheck"] = { + "test": ["CMD-SHELL", "cmd arg1", "arg2"], + } + + with self.assertRaises(ValueError): + await container_to_args(c, cnt)