Merge pull request #1087 from lisongmin/feat-support-network-scoped-aliases

Support network scoped service aliases
This commit is contained in:
Povilas Kanapickas 2024-12-22 00:54:26 +02:00 committed by GitHub
commit 75d7be2b7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 350 additions and 248 deletions

View File

@ -0,0 +1 @@
Support network scoped service aliases.

View File

@ -0,0 +1,2 @@
Improved error detection by rejecting service definitions that contain both "network_mode" and
"networks" keys, which is not allowed.

View File

@ -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())
@ -877,161 +878,141 @@ async def assert_cnt_nets(compose, cnt):
await compose.podman.output([], "network", ["exists", net_name])
def get_net_args(compose, cnt):
service_name = cnt["service_name"]
def get_net_args_from_network_mode(compose, cnt):
net_args = []
is_bridge = False
mac_address = cnt.get("mac_address")
net = cnt.get("network_mode")
service_name = cnt["service_name"]
if "networks" in cnt:
raise ValueError(
f"networks and network_mode must not be present in the same service [{service_name}]"
)
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"):
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]
mac_address = cnt.get("mac_address")
if mac_address:
net_options.append(f"mac={mac_address}")
net = f"{net}," if ":" in net else f"{net}:"
net_args.append(f"--network={net}{','.join(net_options)}")
else:
log.fatal("unknown network_mode [%s]", net)
sys.exit(1)
return net_args
def get_net_args(compose, cnt):
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_networks(compose, cnt):
net_args = []
mac_address = cnt.get("mac_address")
service_name = cnt["service_name"]
aliases_on_container = [service_name]
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]
if mac_address:
net_options.append(f"mac={mac_address}")
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:
is_bridge = True
cnt_nets = cnt.get("networks")
multiple_nets = {net: net_config or {} for net, net_config in multiple_nets.items()}
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
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 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"
)
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 {}
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}
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")
aliases_on_net = norm_as_list(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:
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}"])
net_args.append(f"--network={net_name}")
return net_args
@ -3491,7 +3472,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",
)

View File

@ -0,0 +1,33 @@
---
networks:
net0:
ipam:
config:
- subnet: "172.19.3.0/24"
net1:
ipam:
config:
- subnet: "172.19.4.0/24"
services:
web1:
image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/tmp", "-p", "8001"]
networks:
net0:
ipv4_address: "172.19.3.11"
aliases:
- secure-web
net1:
ipv4_address: "172.19.4.11"
aliases:
- insecure-web
utils-net0:
image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/tmp", "-p", "8001"]
networks:
- net0
utils-net1:
image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/tmp", "-p", "8001"]
networks:
- net1

View File

@ -0,0 +1,84 @@
# SPDX-License-Identifier: GPL-2.0
# pylint: disable=redefined-outer-name
import os
import unittest
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
class TestPodmanComposeNetworkScopedAliases(RunSubprocessMixin, unittest.TestCase):
@staticmethod
def compose_file():
"""Returns the path to the compose file used for this test module"""
return os.path.join(test_path(), "network_scoped_aliases", "docker-compose.yaml")
def test_network_scoped_aliases(self):
try:
self.up()
self.verify()
finally:
self.down()
def up(self):
up_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
self.compose_file(),
"up",
"-d",
"--force-recreate",
]
self.run_subprocess_assert_returncode(up_cmd)
def down(self):
down_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
self.compose_file(),
"kill",
"-a",
]
self.run_subprocess(down_cmd)
def verify(self):
expected_results = [
("utils-net0", "web1", ["172.19.3.11"]),
("utils-net0", "secure-web", ["172.19.3.11"]),
("utils-net0", "insecure-web", []),
("utils-net1", "web1", ["172.19.4.11"]),
("utils-net1", "secure-web", []),
("utils-net1", "insecure-web", ["172.19.4.11"]),
]
for utils, service, expected_result in expected_results:
cmd = [
podman_compose_path(),
"-f",
self.compose_file(),
"exec",
utils,
"nslookup",
service,
]
out, _, _ = self.run_subprocess(cmd)
addresses = self.parse_dnslookup(out.decode())
self.assertEqual(addresses, expected_result)
def parse_dnslookup(self, output):
lines = output.splitlines()
addresses = []
for line in lines:
if line.startswith("Address"):
addr = line.split(":", 1)[1].strip()
if ":" not in addr:
addresses.append(addr)
return list(sorted(set(addresses)))

View File

@ -51,8 +51,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--name=project_name_service_name1",
"-d",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"busybox",
],
)
@ -69,8 +68,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--name=project_name_service_name1",
"-d",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"--runtime",
"runsc",
"busybox",
@ -92,8 +90,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--name=project_name_service_name1",
"-d",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"--pid",
"host",
"busybox",
@ -170,8 +165,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--name=project_name_service_name1",
"-d",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"--no-hosts",
"busybox",
],
@ -287,8 +277,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--name=project_name_service_name1",
"-d",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"busybox",
],
)
@ -428,8 +413,7 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
"/dev/ttyS0",
"--device-cgroup-rule",
"c 100:200 rwm",
"--network=bridge",
"--network-alias=service_name",
"--network=bridge:alias=service_name",
"busybox",
],
)

View File

@ -36,8 +36,7 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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 @@ class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
[
"--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",

View File

@ -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,22 @@ class TestGetNetArgs(unittest.TestCase):
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()
mac_address = "11:22:33:44:55:66"
container["mac_address"] = mac_address
compose.default_net = None
expected_args = [
f"--network=bridge:alias={SERVICE_NAME},mac={mac_address}",
]
args = get_net_args(compose, container)
self.assertListEqual(expected_args, args)
@ -53,8 +70,7 @@ class TestGetNetArgs(unittest.TestCase):
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 +82,18 @@ class TestGetNetArgs(unittest.TestCase):
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 +105,7 @@ class TestGetNetArgs(unittest.TestCase):
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 +117,7 @@ class TestGetNetArgs(unittest.TestCase):
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 +130,7 @@ class TestGetNetArgs(unittest.TestCase):
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 +143,20 @@ class TestGetNetArgs(unittest.TestCase):
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 +167,8 @@ class TestGetNetArgs(unittest.TestCase):
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 +179,8 @@ class TestGetNetArgs(unittest.TestCase):
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 +193,8 @@ class TestGetNetArgs(unittest.TestCase):
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 +207,8 @@ class TestGetNetArgs(unittest.TestCase):
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 +225,8 @@ class TestGetNetArgs(unittest.TestCase):
}
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 +248,7 @@ class TestGetNetArgs(unittest.TestCase):
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 +269,20 @@ class TestGetNetArgs(unittest.TestCase):
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},mac=11:22:33:44:55:66"]),
(
"bridge:ip=10.88.0.3",
[f"--network=bridge:ip=10.88.0.3,alias={SERVICE_NAME},mac=11:22:33:44:55:66"],
),
("host", ["--network=host"]),
("none", ["--network=none"]),
("slirp4netns", ["--network=slirp4netns"]),
@ -271,6 +298,10 @@ class TestGetNetArgs(unittest.TestCase):
container = get_minimal_container()
container["network_mode"] = network_mode
mac_address = "11:22:33:44:55:66"
container["network_mode"] = network_mode
container["mac_address"] = mac_address
args = get_net_args(compose, container)
self.assertListEqual(expected_args, args)