From 83e684b406f6acb781c57f46877a885e6d63ac50 Mon Sep 17 00:00:00 2001 From: Songmin Li Date: Wed, 18 Dec 2024 16:23:23 +0800 Subject: [PATCH] Support network scoped service aliases Signed-off-by: Songmin Li --- newsfragments/network-scoped-aliases.feature | 1 + podman_compose.py | 265 +++++++++---------- tests/unit/test_container_to_args.py | 66 ++--- tests/unit/test_container_to_args_secrets.py | 24 +- tests/unit/test_get_net_args.py | 105 +++++--- 5 files changed, 212 insertions(+), 249 deletions(-) create mode 100644 newsfragments/network-scoped-aliases.feature diff --git a/newsfragments/network-scoped-aliases.feature b/newsfragments/network-scoped-aliases.feature new file mode 100644 index 00000000..49058fe0 --- /dev/null +++ b/newsfragments/network-scoped-aliases.feature @@ -0,0 +1 @@ +Support network scoped service aliases. diff --git a/podman_compose.py b/podman_compose.py index 81dcba39..c0b38617 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -855,8 +855,9 @@ async def assert_cnt_nets(compose, cnt): create missing networks """ net = cnt.get("network_mode") - if net and not net.startswith("bridge"): + if net: return + cnt_nets = cnt.get("networks") if cnt_nets and isinstance(cnt_nets, dict): cnt_nets = list(cnt_nets.keys()) @@ -878,160 +879,130 @@ async def assert_cnt_nets(compose, cnt): def get_net_args(compose, cnt): - service_name = cnt["service_name"] - net_args = [] - is_bridge = False - mac_address = cnt.get("mac_address") net = cnt.get("network_mode") if net: - if net == "none": - is_bridge = False - net_args.append("--network=none") - elif net == "host": - net_args.append(f"--network={net}") - elif net.startswith("slirp4netns"): # Note: podman-specific network mode - net_args.append(f"--network={net}") - elif net == "private": # Note: podman-specific network mode - net_args.append("--network=private") - elif net.startswith("pasta"): # Note: podman-specific network mode - net_args.append(f"--network={net}") - elif net.startswith("ns:"): # Note: podman-specific network mode - net_args.append(f"--network={net}") - elif net.startswith("service:"): - other_srv = net.split(":", 1)[1].strip() - other_cnt = compose.container_names_by_service[other_srv][0] - net_args.append(f"--network=container:{other_cnt}") - elif net.startswith("container:"): - other_cnt = net.split(":", 1)[1].strip() - net_args.append(f"--network=container:{other_cnt}") - elif net.startswith("bridge"): - is_bridge = True - else: - log.fatal("unknown network_mode [%s]", net) - sys.exit(1) + return get_net_args_from_network_mode(compose, cnt) + + return get_net_args_from_networks(compose, cnt) + + +def get_net_args_from_network_mode(compose, cnt): + net_args = [] + net = cnt.get("network_mode") + + if net == "none": + net_args.append("--network=none") + elif net == "host": + net_args.append(f"--network={net}") + elif net.startswith("slirp4netns"): # Note: podman-specific network mode + net_args.append(f"--network={net}") + elif net == "private": # Note: podman-specific network mode + net_args.append("--network=private") + elif net.startswith("pasta"): # Note: podman-specific network mode + net_args.append(f"--network={net}") + elif net.startswith("ns:"): # Note: podman-specific network mode + net_args.append(f"--network={net}") + elif net.startswith("service:"): + other_srv = net.split(":", 1)[1].strip() + other_cnt = compose.container_names_by_service[other_srv][0] + net_args.append(f"--network=container:{other_cnt}") + elif net.startswith("container:"): + other_cnt = net.split(":", 1)[1].strip() + net_args.append(f"--network=container:{other_cnt}") + elif net.startswith("bridge"): + service_name = cnt["service_name"] + aliases_on_container = [service_name] + if cnt.get("_aliases"): + aliases_on_container.extend(cnt.get("_aliases")) + net_options = [f"alias={alias}" for alias in aliases_on_container] + net = f"{net}," if ":" in net else f"{net}:" + net_args.append(f"--network={net}{'.'.join(net_options)}") else: - is_bridge = True - cnt_nets = cnt.get("networks") + log.fatal("unknown network_mode [%s]", net) + sys.exit(1) - aliases = [service_name] - # NOTE: from podman manpage: - # NOTE: A container will only have access to aliases on the first network - # that it joins. This is a limitation that will be removed in a later - # release. - ip = None - ip6 = None - ip_assignments = 0 + return net_args + + +def get_net_args_from_networks(compose, cnt): + net_args = [] + mac_address = cnt.get("mac_address") + + service_name = cnt["service_name"] + aliases_on_container = [service_name] if cnt.get("_aliases"): - aliases.extend(cnt.get("_aliases")) - if cnt_nets and isinstance(cnt_nets, dict): - prioritized_cnt_nets = [] - # cnt_nets is {net_key: net_value, ...} - for net_key, net_value in cnt_nets.items(): - net_value = net_value or {} - aliases.extend(norm_as_list(net_value.get("aliases"))) - if net_value.get("ipv4_address") is not None: - ip_assignments = ip_assignments + 1 - if net_value.get("ipv6_address") is not None: - ip_assignments = ip_assignments + 1 - - if not ip: - ip = net_value.get("ipv4_address") - if not ip6: - ip6 = net_value.get("ipv6_address") - net_priority = net_value.get("priority", 0) - prioritized_cnt_nets.append(( - net_priority, - net_key, - )) - # sort dict by priority - prioritized_cnt_nets.sort(reverse=True) - cnt_nets = [net_key for _, net_key in prioritized_cnt_nets] - cnt_nets = norm_as_list(cnt_nets or compose.default_net) - net_names = [] - for net in cnt_nets: - net_desc = compose.networks[net] or {} + aliases_on_container.extend(cnt.get("_aliases")) + + multiple_nets = cnt.get("networks", {}) + if not multiple_nets: + if not compose.default_net: + # The bridge mode in podman is using the `podman` network. + # It seems weird, but we should keep this behavior to avoid + # breaking changes. + net_options = [f"alias={alias}" for alias in aliases_on_container] + net_args.append(f"--network=bridge:{'.'.join(net_options)}") + return net_args + + multiple_nets = {compose.default_net: {}} + + # networks can be specified as a dict with config per network or as a plain list without + # config. Support both cases by converting the plain list to a dict with empty config. + if is_list(multiple_nets): + multiple_nets = {net: {} for net in multiple_nets} + else: + multiple_nets = {net: net_config or {} for net, net_config in multiple_nets.items()} + + # if a mac_address was specified on the container level, we need to check that it is not + # specified on the network level as well + if mac_address is not None: + for net_config_ in multiple_nets.values(): + network_mac = net_config_.get("x-podman.mac_address") + if network_mac is not None: + raise RuntimeError( + f"conflicting mac addresses {mac_address} and {network_mac}:" + "specifying mac_address on both container and network level " + "is not supported" + ) + + for net_, net_config_ in multiple_nets.items(): + net_desc = compose.networks.get(net_) or {} is_ext = net_desc.get("external") ext_desc = is_ext if isinstance(is_ext, str) else {} - default_net_name = default_network_name_for_project(compose, net, is_ext) + default_net_name = default_network_name_for_project(compose, net_, is_ext) net_name = ext_desc.get("name") or net_desc.get("name") or default_net_name - net_names.append(net_name) - net_names_str = ",".join(net_names) - - # TODO: add support for per-interface aliases - # See https://docs.docker.com/compose/compose-file/compose-file-v3/#aliases - # Even though podman accepts network-specific aliases (e.g., --network=bridge:alias=foo, - # podman currently ignores this if a per-container network-alias is set; as pdoman-compose - # always sets a network-alias to the container name, is currently doesn't make sense to - # implement this. - multiple_nets = cnt.get("networks") - if multiple_nets and len(multiple_nets) > 1: - # networks can be specified as a dict with config per network or as a plain list without - # config. Support both cases by converting the plain list to a dict with empty config. - if is_list(multiple_nets): - multiple_nets = {net: {} for net in multiple_nets} - else: - multiple_nets = {net: net_config or {} for net, net_config in multiple_nets.items()} - - # if a mac_address was specified on the container level, we need to check that it is not - # specified on the network level as well - if mac_address is not None: - for net_config_ in multiple_nets.values(): - network_mac = net_config_.get("x-podman.mac_address") - if network_mac is not None: - raise RuntimeError( - f"conflicting mac addresses {mac_address} and {network_mac}:" - "specifying mac_address on both container and network level " - "is not supported" - ) - for net_, net_config_ in multiple_nets.items(): - net_desc = compose.networks[net_] or {} - is_ext = net_desc.get("external") - ext_desc = is_ext if isinstance(is_ext, str) else {} - default_net_name = default_network_name_for_project(compose, net_, is_ext) - net_name = ext_desc.get("name") or net_desc.get("name") or default_net_name - - ipv4 = net_config_.get("ipv4_address") - ipv6 = net_config_.get("ipv6_address") - # custom extension; not supported by docker-compose v3 - mac = net_config_.get("x-podman.mac_address") - - # if a mac_address was specified on the container level, apply it to the first network - # This works for Python > 3.6, because dict insert ordering is preserved, so we are - # sure that the first network we encounter here is also the first one specified by - # the user - if mac is None and mac_address is not None: - mac = mac_address - mac_address = None - - net_options = [] - if ipv4: - net_options.append(f"ip={ipv4}") - if ipv6: - net_options.append(f"ip={ipv6}") - if mac: - net_options.append(f"mac={mac}") - - if net_options: - net_args.append(f"--network={net_name}:" + ",".join(net_options)) - else: - net_args.append(f"--network={net_name}") - else: - if is_bridge: - if net_names_str: - net_args.append(f"--network={net_names_str}") - else: - net_args.append("--network=bridge") - if ip: - net_args.append(f"--ip={ip}") - if ip6: - net_args.append(f"--ip6={ip6}") - if mac_address: - net_args.append(f"--mac-address={mac_address}") - - if is_bridge: - for alias in aliases: - net_args.extend([f"--network-alias={alias}"]) + ipv4 = net_config_.get("ipv4_address") + ipv6 = net_config_.get("ipv6_address") + + mac = net_config_.get("x-podman.mac_address") + aliases_on_net = net_config_.get("aliases") + + # if a mac_address was specified on the container level, apply it to the first network + # This works for Python > 3.6, because dict insert ordering is preserved, so we are + # sure that the first network we encounter here is also the first one specified by + # the user + if mac is None and mac_address is not None: + mac = mac_address + mac_address = None + + net_options = [] + if ipv4: + net_options.append(f"ip={ipv4}") + if ipv6: + net_options.append(f"ip6={ipv6}") + if mac: + net_options.append(f"mac={mac}") + + # Container level service aliases + net_options.extend([f"alias={alias}" for alias in aliases_on_container]) + # network level service aliases + if aliases_on_net: + net_options.extend([f"alias={alias}" for alias in aliases_on_net]) + + if net_options: + net_args.append(f"--network={net_name}:" + ",".join(net_options)) + else: + net_args.append(f"--network={net_name}") return net_args @@ -3481,7 +3452,7 @@ def compose_logs_parse(parser): parser.add_argument("-t", "--timestamps", action="store_true", help="Show timestamps.") parser.add_argument( "--tail", - help="Number of lines to show from the end of the logs for each " "container.", + help="Number of lines to show from the end of the logs for each container.", type=str, default="all", ) diff --git a/tests/unit/test_container_to_args.py b/tests/unit/test_container_to_args.py index b556b237..beb21cad 100644 --- a/tests/unit/test_container_to_args.py +++ b/tests/unit/test_container_to_args.py @@ -51,8 +51,7 @@ async def test_minimal(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -69,8 +68,7 @@ async def test_runtime(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--runtime", "runsc", "busybox", @@ -92,8 +90,7 @@ async def test_sysctl_list(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--sysctl", "net.core.somaxconn=1024", "--sysctl", @@ -117,8 +114,7 @@ async def test_sysctl_map(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--sysctl", "net.core.somaxconn=1024", "--sysctl", @@ -149,8 +145,7 @@ async def test_pid(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--pid", "host", "busybox", @@ -170,8 +165,7 @@ async def test_http_proxy(self): "--name=project_name_service_name1", "-d", "--http-proxy=false", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -197,8 +191,7 @@ async def test_uidmaps_extension(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", '--uidmap', '1000:1000:1', '--uidmap', @@ -219,8 +212,7 @@ async def test_gidmaps_extension(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", '--gidmap', '1000:1000:1', '--gidmap', @@ -242,8 +234,7 @@ async def test_rootfs_extension(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--rootfs", "/path/to/rootfs", ], @@ -261,8 +252,7 @@ async def test_no_hosts_extension(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--no-hosts", "busybox", ], @@ -287,8 +277,7 @@ async def test_env_file_str(self): "ZZVAR2=podman-rocks-124", "-e", "ZZVAR3=podman-rocks-125", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -321,8 +310,7 @@ async def test_env_file_str_array_one_path(self): "ZZVAR2=podman-rocks-124", "-e", "ZZVAR3=podman-rocks-125", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -351,8 +339,7 @@ async def test_env_file_str_array_two_paths(self): "ZZVAR1=podman-rocks-223", "-e", "ZZVAR2=podman-rocks-224", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -376,8 +363,7 @@ async def test_env_file_obj_required(self): "ZZVAR2=podman-rocks-124", "-e", "ZZVAR3=podman-rocks-125", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -403,8 +389,7 @@ async def test_env_file_obj_optional(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -428,8 +413,7 @@ async def test_gpu_count_all(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--device", "nvidia.com/gpu=all", "--security-opt=label=disable", @@ -463,8 +447,7 @@ async def test_gpu_count_specific(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--device", "nvidia.com/gpu=0", "--device", @@ -500,8 +483,7 @@ async def test_gpu_device_ids_all(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--device", "nvidia.com/gpu=all", "--security-opt=label=disable", @@ -535,8 +517,7 @@ async def test_gpu_device_ids_specific(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--device", "nvidia.com/gpu=1", "--device", @@ -581,8 +562,7 @@ async def test_selinux_volume(self, prefer_volume, selinux_type, expected_additi "--name=project_name_service_name1", "-d", *expected_additional_args, - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) @@ -606,8 +586,7 @@ async def test_network_default_name(self, name, is_compat, project_name, expecte [ "--name=project_name_service_name1", "-d", - f"--network={expected_network_name}", - "--network-alias=service_name", + f"--network={expected_network_name}:alias=service_name", "busybox", ], ) @@ -629,8 +608,7 @@ async def test_device(self): "/dev/ttyS0", "--device-cgroup-rule", "c 100:200 rwm", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "busybox", ], ) diff --git a/tests/unit/test_container_to_args_secrets.py b/tests/unit/test_container_to_args_secrets.py index 86540a73..084839b0 100644 --- a/tests/unit/test_container_to_args_secrets.py +++ b/tests/unit/test_container_to_args_secrets.py @@ -36,8 +36,7 @@ async def test_pass_secret_as_env_variable(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret,type=env,target=ENV_SECRET", "busybox", @@ -68,8 +67,7 @@ async def test_secret_as_env_external_true_has_no_name(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret,type=env,target=ENV_SECRET", "busybox", @@ -152,8 +150,7 @@ async def test_secret_string_no_external_name_in_declared_secrets(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret_name", "busybox", @@ -191,8 +188,7 @@ async def test_secret_string_options_external_name_in_declared_secrets(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret_name,uid=103,gid=103,mode=400", "busybox", @@ -257,8 +253,7 @@ async def test_secret_target_does_not_match_secret_name_secret_type_env(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret_name,type=env,target=does_not_equal_secret_name", "busybox", @@ -289,8 +284,7 @@ async def test_secret_target_matches_secret_name_secret_type_not_env(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--secret", "my_secret_name,type=does_not_equal_env", "busybox", @@ -361,8 +355,7 @@ async def test_file_secret( [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--volume", expected_volume_ref, "busybox", @@ -398,8 +391,7 @@ async def test_file_secret_unused_params_warning(self): [ "--name=project_name_service_name1", "-d", - "--network=bridge", - "--network-alias=service_name", + "--network=bridge:alias=service_name", "--volume", repo_root() + "/test_dirname/my_secret:/run/secrets/unused_params_warning:ro,rprivate,rbind", diff --git a/tests/unit/test_get_net_args.py b/tests/unit/test_get_net_args.py index 586067f0..7084a30b 100644 --- a/tests/unit/test_get_net_args.py +++ b/tests/unit/test_get_net_args.py @@ -24,6 +24,9 @@ def get_networked_compose(num_networks=1): "enable_ipv6": True, } + if num_networks == 1: + compose.default_net = "net0" + return compose @@ -41,8 +44,19 @@ def test_minimal(self): container = get_minimal_container() expected_args = [ - "--network=bridge", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME}", + ] + args = get_net_args(compose, container) + self.assertListEqual(expected_args, args) + + def test_default_net_is_None(self): + compose = get_networked_compose() + container = get_minimal_container() + + compose.default_net = None + + expected_args = [ + f"--network=bridge:alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -53,8 +67,7 @@ def test_one_net(self): container["networks"] = {"net0": {}} expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -66,10 +79,18 @@ def test_alias(self): container["_aliases"] = ["alias1", "alias2"] expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--network-alias={SERVICE_NAME}", - "--network-alias=alias1", - "--network-alias=alias2", + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME},alias=alias1,alias=alias2", + ] + args = get_net_args(compose, container) + self.assertListEqual(expected_args, args) + + def test_aliases_on_network_scope(self): + compose = get_networked_compose() + container = get_minimal_container() + container["networks"] = {"net0": {"aliases": ["alias1"]}} + + expected_args = [ + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME},alias=alias1", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -81,9 +102,7 @@ def test_one_ipv4(self): container["networks"] = {"net0": {"ipv4_address": ip}} expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--ip={ip}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:ip={ip},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertEqual(expected_args, args) @@ -95,9 +114,7 @@ def test_one_ipv6(self): container["networks"] = {"net0": {"ipv6_address": ipv6_address}} expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--ip6={ipv6_address}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:ip6={ipv6_address},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -110,9 +127,7 @@ def test_one_mac(self): container["mac_address"] = mac expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--mac-address={mac}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:mac={mac},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -125,9 +140,20 @@ def test_one_mac_two_nets(self): container["mac_address"] = mac expected_args = [ - f"--network={PROJECT_NAME}_net0:mac={mac}", - f"--network={PROJECT_NAME}_net1", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:mac={mac},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:alias={SERVICE_NAME}", + ] + args = get_net_args(compose, container) + self.assertListEqual(expected_args, args) + + def test_mac_on_network(self): + mac = "00:11:22:33:44:55" + compose = get_networked_compose() + container = get_minimal_container() + container["networks"] = {"net0": {"x-podman.mac_address": mac}} + + expected_args = [ + f"--network={PROJECT_NAME}_net0:mac={mac},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -138,9 +164,8 @@ def test_two_nets_as_dict(self): container["networks"] = {"net0": {}, "net1": {}} expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--network={PROJECT_NAME}_net1", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -151,9 +176,8 @@ def test_two_nets_as_list(self): container["networks"] = ["net0", "net1"] expected_args = [ - f"--network={PROJECT_NAME}_net0", - f"--network={PROJECT_NAME}_net1", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -166,9 +190,8 @@ def test_two_ipv4(self): container["networks"] = {"net0": {"ipv4_address": ip0}, "net1": {"ipv4_address": ip1}} expected_args = [ - f"--network={PROJECT_NAME}_net0:ip={ip0}", - f"--network={PROJECT_NAME}_net1:ip={ip1}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:ip={ip0},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:ip={ip1},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -181,9 +204,8 @@ def test_two_ipv6(self): container["networks"] = {"net0": {"ipv6_address": ip0}, "net1": {"ipv6_address": ip1}} expected_args = [ - f"--network={PROJECT_NAME}_net0:ip={ip0}", - f"--network={PROJECT_NAME}_net1:ip={ip1}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:ip6={ip0},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:ip6={ip1},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -200,9 +222,8 @@ def test_two_mac(self): } expected_args = [ - f"--network={PROJECT_NAME}_net0:mac={mac0}", - f"--network={PROJECT_NAME}_net1:mac={mac1}", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:mac={mac0},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:mac={mac1},alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @@ -224,7 +245,7 @@ def test_mixed_mac(self): container["mac_address"] = mac_1 expected_exception = ( - r"specifying mac_address on both container and network level " r"is not supported" + r"specifying mac_address on both container and network level is not supported" ) self.assertRaisesRegex(RuntimeError, expected_exception, get_net_args, compose, container) @@ -245,17 +266,17 @@ def test_mixed_config(self): container["mac_address"] = mac expected_args = [ - f"--network={PROJECT_NAME}_net0:ip={ip4_0},ip={ip6_0},mac={mac}", - f"--network={PROJECT_NAME}_net1:ip={ip4_1}", - f"--network={PROJECT_NAME}_net2:ip={ip6_2}", - f"--network={PROJECT_NAME}_net3", - f"--network-alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net0:ip={ip4_0},ip6={ip6_0},mac={mac},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net1:ip={ip4_1},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net2:ip6={ip6_2},alias={SERVICE_NAME}", + f"--network={PROJECT_NAME}_net3:alias={SERVICE_NAME}", ] args = get_net_args(compose, container) self.assertListEqual(expected_args, args) @parameterized.expand([ - ("bridge", ["--network=bridge", f"--network-alias={SERVICE_NAME}"]), + ("bridge", [f"--network=bridge:alias={SERVICE_NAME}"]), + ("bridge:ip=10.88.0.3", [f"--network=bridge:ip=10.88.0.3,alias={SERVICE_NAME}"]), ("host", ["--network=host"]), ("none", ["--network=none"]), ("slirp4netns", ["--network=slirp4netns"]),