diff --git a/docs/Extensions.md b/docs/Extensions.md index 300e071a..c5575d1a 100644 --- a/docs/Extensions.md +++ b/docs/Extensions.md @@ -110,6 +110,26 @@ x-podman: By default `default_net_name_compat` is `false`. This will change to `true` at some point and the setting will be removed. +## Compatibility of default network behavior between docker-compose and podman-compose + +When there is no network defined (neither network-mode nor networks) in service, +The behavior of default network in docker-compose and podman-compose are different. + +| Top-level networks | podman-compose | docker-compose | +| ------------------------------ | -------------------------- | -------------- | +| No networks | default | default | +| One network named net0 | net0 | default | +| Two networks named net0, net1 | podman(`--network=bridge`) | default | +| Contains network named default | default | default | + +To enable compatibility between docker-compose and podman-compose, specify +`default_net_behavior_compat: true` under global `x-podman` key: + +```yaml +x-podman: + default_net_behavior_compat: true +``` + ## Custom pods management Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod. diff --git a/newsfragments/default_net_behavior_compat.feature b/newsfragments/default_net_behavior_compat.feature new file mode 100644 index 00000000..133e9967 --- /dev/null +++ b/newsfragments/default_net_behavior_compat.feature @@ -0,0 +1,2 @@ +Support docker-compose like default network behavior when no network defined in service via `default_net_behavior_compat` feature flag. + diff --git a/podman_compose.py b/podman_compose.py index 81dcba39..09086317 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -1998,13 +1998,23 @@ def _parse_compose_file(self): nets = compose.get("networks", {}) if not nets: nets["default"] = None + self.networks = nets - if len(self.networks) == 1: - self.default_net = list(nets.keys())[0] - elif "default" in nets: - self.default_net = "default" + if compose.get("x-podman", {}).get("default_net_behavior_compat", False): + # If there is no network_mode and networks in service, + # docker-compose will create default network named '_default' + # and add the service to the default network. + # So we always set `default_net = 'default'` for compatibility + if "default" not in self.networks: + self.networks["default"] = None else: - self.default_net = None + if len(self.networks) == 1: + self.default_net = list(nets.keys())[0] + elif "default" in nets: + self.default_net = "default" + else: + self.default_net = None + allnets = set() for name, srv in services.items(): srv_nets = srv.get("networks", self.default_net) diff --git a/tests/integration/default_net_behavior/docker-compose_no_nets.yaml b/tests/integration/default_net_behavior/docker-compose_no_nets.yaml new file mode 100644 index 00000000..6ac63ff4 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_no_nets.yaml @@ -0,0 +1,4 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ diff --git a/tests/integration/default_net_behavior/docker-compose_no_nets_compat.yaml b/tests/integration/default_net_behavior/docker-compose_no_nets_compat.yaml new file mode 100644 index 00000000..d25f05d3 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_no_nets_compat.yaml @@ -0,0 +1,7 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +x-podman: + default_net_behavior_compat: true diff --git a/tests/integration/default_net_behavior/docker-compose_one_net.yaml b/tests/integration/default_net_behavior/docker-compose_one_net.yaml new file mode 100644 index 00000000..0e8c1d2c --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_one_net.yaml @@ -0,0 +1,7 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} diff --git a/tests/integration/default_net_behavior/docker-compose_one_net_compat.yaml b/tests/integration/default_net_behavior/docker-compose_one_net_compat.yaml new file mode 100644 index 00000000..3af9dea5 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_one_net_compat.yaml @@ -0,0 +1,10 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} + +x-podman: + default_net_behavior_compat: true diff --git a/tests/integration/default_net_behavior/docker-compose_two_nets.yaml b/tests/integration/default_net_behavior/docker-compose_two_nets.yaml new file mode 100644 index 00000000..1a956a00 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_two_nets.yaml @@ -0,0 +1,8 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} + net1: {} diff --git a/tests/integration/default_net_behavior/docker-compose_two_nets_compat.yaml b/tests/integration/default_net_behavior/docker-compose_two_nets_compat.yaml new file mode 100644 index 00000000..04ec40d7 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_two_nets_compat.yaml @@ -0,0 +1,11 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} + net1: {} + +x-podman: + default_net_behavior_compat: true diff --git a/tests/integration/default_net_behavior/docker-compose_with_default.yaml b/tests/integration/default_net_behavior/docker-compose_with_default.yaml new file mode 100644 index 00000000..ab949edc --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_with_default.yaml @@ -0,0 +1,9 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} + net1: {} + default: {} diff --git a/tests/integration/default_net_behavior/docker-compose_with_default_compat.yaml b/tests/integration/default_net_behavior/docker-compose_with_default_compat.yaml new file mode 100644 index 00000000..c69088f7 --- /dev/null +++ b/tests/integration/default_net_behavior/docker-compose_with_default_compat.yaml @@ -0,0 +1,12 @@ +services: + web: + image: busybox + command: httpd -f -p 8123 -h /tmp/ + +networks: + net0: {} + net1: {} + default: {} + +x-podman: + default_net_behavior_compat: true diff --git a/tests/integration/test_podman_compose_default_net_behavior.py b/tests/integration/test_podman_compose_default_net_behavior.py new file mode 100644 index 00000000..367562a6 --- /dev/null +++ b/tests/integration/test_podman_compose_default_net_behavior.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0 + +import os +import unittest + +from parameterized import parameterized + +from tests.integration.test_podman_compose import podman_compose_path +from tests.integration.test_podman_compose import test_path +from tests.integration.test_utils import RunSubprocessMixin + + +def compose_yaml_path(scenario): + return os.path.join( + os.path.join(test_path(), "default_net_behavior"), f"docker-compose_{scenario}.yaml" + ) + + +class TestComposeDefaultNetBehavior(unittest.TestCase, RunSubprocessMixin): + @parameterized.expand([ + ('no_nets', 'default_net_behavior_default'), + ('one_net', 'default_net_behavior_net0'), + ('two_nets', 'podman'), + ('with_default', 'default_net_behavior_default'), + ('no_nets_compat', 'default_net_behavior_default'), + ('one_net_compat', 'default_net_behavior_default'), + ('two_nets_compat', 'default_net_behavior_default'), + ('with_default_compat', 'default_net_behavior_default'), + ]) + def test_nethost(self, scenario, default_net): + try: + self.run_subprocess_assert_returncode( + [podman_compose_path(), "-f", compose_yaml_path(scenario), "up", "-d"], + ) + + container_id, _ = self.run_subprocess_assert_returncode( + [ + podman_compose_path(), + "-f", + compose_yaml_path(scenario), + "ps", + "--format", + '{{.ID}}', + ], + ) + container_id = container_id.decode('utf-8').split('\n')[0] + output, _ = self.run_subprocess_assert_returncode( + [ + "podman", + "inspect", + container_id, + "--format", + "{{range $key, $value := .NetworkSettings.Networks }}{{ $key }}\n{{ end }}", + ], + ) + self.assertEqual(output.decode('utf-8').strip(), default_net) + finally: + self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(scenario), + "down", + "-t", + "0", + ])