Add a way to remove dashes in default network names

This is the behavior exhibited by docker compose. The network names are
user-visible through external networks, so previously anyone who
migrated from docker-compose needed to change their configuration. Now
it is possible to select compatibility via a flag in x-podman global
dictionary.

Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
This commit is contained in:
Povilas Kanapickas 2024-07-06 18:32:10 +03:00
parent 67c5352c3a
commit 5bf4c0fdbe
4 changed files with 66 additions and 15 deletions

View File

@ -91,6 +91,23 @@ The options to the network modes are passed to the `--network` option of the `po
as-is.
## Compatibility of default network names between docker-compose and podman-compose
Current versions of podman-compose may produce different default external network names than
docker-compose under certain conditions. Specifically, docker-compose removes dashes (`-` character)
from project name.
To enable compatibility between docker-compose and podman-compose, specify
`default_net_name_compat: true` under global `x-podman` key:
```
x-podman:
default_net_name_compat: true
```
By default `default_net_name_compat` is `false`. This will change to `true` at some point and the
setting will be removed.
## Custom pods management
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.

View File

@ -0,0 +1 @@
Added a way to get compatibility of default network names with docker compose. This is selected by setting `default_net_name_compat: true` on `x-podman` global dictionary.

View File

@ -336,9 +336,14 @@ def norm_ulimit(inner_value):
return inner_value
def default_network_name_for_project(proj_name, net, is_ext):
# docker-compose removes dashes from project name when building network name
return net if is_ext else f"{proj_name}_{net}"
def default_network_name_for_project(compose, proj_name, net, is_ext):
if is_ext:
return net
default_net_name_compat = compose.x_podman.get("default_net_name_compat", False)
if default_net_name_compat is True:
return f"{proj_name.replace('-', '')}_{net}"
return f"{proj_name}_{net}"
# def tr_identity(project_name, given_containers):
@ -850,7 +855,7 @@ async def assert_cnt_nets(compose, cnt):
net_desc = nets[net] or {}
is_ext = net_desc.get("external", None)
ext_desc = is_ext if is_dict(is_ext) else {}
default_net_name = default_network_name_for_project(proj_name, net, is_ext)
default_net_name = default_network_name_for_project(compose, proj_name, net, is_ext)
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
try:
await compose.podman.output([], "network", ["exists", net_name])
@ -939,7 +944,7 @@ def get_net_args(compose, cnt):
net_desc = nets[net] or {}
is_ext = net_desc.get("external", None)
ext_desc = is_ext if is_dict(is_ext) else {}
default_net_name = default_network_name_for_project(proj_name, net, is_ext)
default_net_name = default_network_name_for_project(compose, proj_name, net, is_ext)
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
net_names.append(net_name)
net_names_str = ",".join(net_names)
@ -975,7 +980,7 @@ def get_net_args(compose, cnt):
net_desc = nets[net_] or {}
is_ext = net_desc.get("external", None)
ext_desc = is_ext if is_dict(is_ext) else {}
default_net_name = default_network_name_for_project(proj_name, net_, is_ext)
default_net_name = default_network_name_for_project(compose, proj_name, net_, is_ext)
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
ipv4 = net_config_.get("ipv4_address", None)
@ -1677,6 +1682,7 @@ class PodmanCompose:
self.services = None
self.all_services = set()
self.prefer_volume_over_mount = True
self.x_podman = {}
self.merged_yaml = None
self.yaml_hash = ""
self.console_colors = [
@ -1745,15 +1751,9 @@ class PodmanCompose:
if isinstance(retcode, int):
sys.exit(retcode)
def resolve_in_pod(self, compose):
def resolve_in_pod(self):
if self.global_args.in_pod_bool is None:
extension_dict = compose.get("x-podman", None)
if extension_dict is not None:
in_pod_value = extension_dict.get("in_pod", None)
if in_pod_value is not None:
self.global_args.in_pod_bool = in_pod_value
else:
self.global_args.in_pod_bool = True
self.global_args.in_pod_bool = self.x_podman.get("in_pod", True)
# otherwise use `in_pod` value provided by command line
return self.global_args.in_pod_bool
@ -1994,7 +1994,9 @@ class PodmanCompose:
given_containers.sort(key=lambda c: len(c.get("_deps", None) or []))
# log("sorted:", [c["name"] for c in given_containers])
args.in_pod_bool = self.resolve_in_pod(compose)
self.x_podman = compose.get("x-podman", {})
args.in_pod_bool = self.resolve_in_pod()
pods, containers = transform(args, project_name, given_containers)
self.pods = pods
self.containers = containers

View File

@ -17,6 +17,12 @@ def create_compose_mock(project_name="test_project_name"):
compose.prefer_volume_over_mount = False
compose.default_net = None
compose.networks = {}
compose.x_podman = {}
async def podman_output(*args, **kwargs):
pass
compose.podman.output = mock.Mock(side_effect=podman_output)
return compose
@ -561,3 +567,28 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"busybox",
],
)
@parameterized.expand([
("not_compat", False, "test_project_name", "test_project_name_network1"),
("compat_no_dash", True, "test_project_name", "test_project_name_network1"),
("compat_dash", True, "test_project-name", "test_projectname_network1"),
])
async def test_network_default_name(self, name, is_compat, project_name, expected_network_name):
c = create_compose_mock(project_name)
c.x_podman = {"default_net_name_compat": is_compat}
c.networks = {'network1': {}}
cnt = get_minimal_container()
cnt['networks'] = ['network1']
args = await container_to_args(c, cnt)
self.assertEqual(
args,
[
"--name=project_name_service_name1",
"-d",
f"--network={expected_network_name}",
"--network-alias=service_name",
"busybox",
],
)