mirror of
https://github.com/containers/podman-compose.git
synced 2025-05-12 18:24:56 +02:00
Merge pull request #867 from baszoetekouw/fix-networks
Fix multiple networks with separately specified ip and mac
This commit is contained in:
commit
e893d06313
2
.gitignore
vendored
2
.gitignore
vendored
@ -47,6 +47,8 @@ coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
test-compose.yaml
|
||||
test-compose-?.yaml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
67
docs/Extensions.md
Normal file
67
docs/Extensions.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Podman specific extensions to the docker-compose format
|
||||
|
||||
Podman-compose supports the following extension to the docker-compose format.
|
||||
|
||||
## Per-network MAC-addresses
|
||||
|
||||
Generic docker-compose files support specification of the MAC address on the container level. If the
|
||||
container has multiple network interfaces, the specified MAC address is applied to the first
|
||||
specified network.
|
||||
|
||||
Podman-compose in addition supports the specification of MAC addresses on a per-network basis. This
|
||||
is done by adding a `podman.mac_address` key to the network configuration in the container. The
|
||||
value of the `podman.mac_address` key is the MAC address to be used for the network interface.
|
||||
|
||||
Specifying a MAC address for the container and for individual networks at the same time is not
|
||||
supported.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
net0:
|
||||
driver: "bridge"
|
||||
ipam:
|
||||
config:
|
||||
- subnet: "192.168.0.0/24"
|
||||
net1:
|
||||
driver: "bridge"
|
||||
ipam:
|
||||
config:
|
||||
- subnet: "192.168.1.0/24"
|
||||
|
||||
services:
|
||||
webserver
|
||||
image: "busybox"
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc", "-p", "8001"]
|
||||
networks:
|
||||
net0:
|
||||
ipv4_address: "192.168.0.10"
|
||||
podman.mac_address: "02:aa:aa:aa:aa:aa"
|
||||
net1:
|
||||
ipv4_address: "192.168.1.10"
|
||||
podman.mac_address: "02:bb:bb:bb:bb:bb"
|
||||
```
|
||||
|
||||
## Podman-specific network modes
|
||||
|
||||
Generic docker-compose supports the following values for `network-mode` for a container:
|
||||
|
||||
- `bridge`
|
||||
- `host`
|
||||
- `none`
|
||||
- `service`
|
||||
- `container`
|
||||
|
||||
In addition, podman-compose supports the following podman-specific values for `network-mode`:
|
||||
|
||||
- `slirp4netns[:<options>,...]`
|
||||
- `ns:<options>`
|
||||
- `pasta[:<options>,...]`
|
||||
- `private`
|
||||
|
||||
The options to the network modes are passed to the `--network` option of the `podman create` command
|
||||
as-is.
|
@ -780,27 +780,29 @@ async def assert_cnt_nets(compose, cnt):
|
||||
def get_net_args(compose, cnt):
|
||||
service_name = cnt["service_name"]
|
||||
net_args = []
|
||||
mac_address = cnt.get("mac_address", None)
|
||||
if mac_address:
|
||||
net_args.extend(["--mac-address", mac_address])
|
||||
is_bridge = False
|
||||
mac_address = cnt.get("mac_address", None)
|
||||
net = cnt.get("network_mode", None)
|
||||
if net:
|
||||
if net == "none":
|
||||
is_bridge = False
|
||||
elif net == "host":
|
||||
net_args.extend(["--network", net])
|
||||
elif net.startswith("slirp4netns:"):
|
||||
net_args.extend(["--network", net])
|
||||
elif net.startswith("ns:"):
|
||||
net_args.extend(["--network", net])
|
||||
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.extend(["--network", f"container:{other_cnt}"])
|
||||
net_args.append(f"--network=container:{other_cnt}")
|
||||
elif net.startswith("container:"):
|
||||
other_cnt = net.split(":", 1)[1].strip()
|
||||
net_args.extend(["--network", f"container:{other_cnt}"])
|
||||
net_args.append(f"--network=container:{other_cnt}")
|
||||
elif net.startswith("bridge"):
|
||||
is_bridge = True
|
||||
else:
|
||||
@ -812,6 +814,7 @@ def get_net_args(compose, cnt):
|
||||
default_net = compose.default_net
|
||||
nets = compose.networks
|
||||
cnt_nets = cnt.get("networks", None)
|
||||
|
||||
aliases = [service_name]
|
||||
# NOTE: from podman manpage:
|
||||
# NOTE: A container will only have access to aliases on the first network
|
||||
@ -856,32 +859,82 @@ def get_net_args(compose, cnt):
|
||||
net_names.append(net_name)
|
||||
net_names_str = ",".join(net_names)
|
||||
|
||||
if ip_assignments > 1:
|
||||
# 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", None)
|
||||
multiple_net_names = multiple_nets.keys()
|
||||
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()}
|
||||
|
||||
for net_ in multiple_net_names:
|
||||
# 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("podman.mac_address", None)
|
||||
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 = nets[net_] or {}
|
||||
is_ext = net_desc.get("external", None)
|
||||
ext_desc = is_ext if is_dict(is_ext) else {}
|
||||
default_net_name = net_ if is_ext else f"{proj_name}_{net_}"
|
||||
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
|
||||
|
||||
ipv4 = multiple_nets[net_].get("ipv4_address", None)
|
||||
ipv6 = multiple_nets[net_].get("ipv6_address", None)
|
||||
if ipv4 is not None and ipv6 is not None:
|
||||
net_args.extend(["--network", f"{net_name}:ip={ipv4},ip={ipv6}"])
|
||||
elif ipv4 is None and ipv6 is not None:
|
||||
net_args.extend(["--network", f"{net_name}:ip={ipv6}"])
|
||||
elif ipv6 is None and ipv4 is not None:
|
||||
net_args.extend(["--network", f"{net_name}:ip={ipv4}"])
|
||||
ipv4 = net_config_.get("ipv4_address", None)
|
||||
ipv6 = net_config_.get("ipv6_address", None)
|
||||
# custom extension; not supported by docker-compose v3
|
||||
mac = net_config_.get("podman.mac_address", None)
|
||||
|
||||
# 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:
|
||||
net_args.extend(["--net", net_names_str, "--network-alias", ",".join(aliases)])
|
||||
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}"])
|
||||
|
||||
return net_args
|
||||
|
||||
|
||||
|
@ -6,9 +6,9 @@ from unittest import mock
|
||||
from podman_compose import container_to_args
|
||||
|
||||
|
||||
def create_compose_mock():
|
||||
def create_compose_mock(project_name="test_project_name"):
|
||||
compose = mock.Mock()
|
||||
compose.project_name = "test_project_name"
|
||||
compose.project_name = project_name
|
||||
compose.dirname = "test_dirname"
|
||||
compose.container_names_by_service.get = mock.Mock(return_value=None)
|
||||
compose.prefer_volume_over_mount = False
|
||||
@ -37,10 +37,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"busybox",
|
||||
],
|
||||
)
|
||||
@ -57,10 +55,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--runtime",
|
||||
"runsc",
|
||||
"busybox",
|
||||
@ -82,10 +78,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--sysctl",
|
||||
"net.core.somaxconn=1024",
|
||||
"--sysctl",
|
||||
@ -109,10 +103,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--sysctl",
|
||||
"net.core.somaxconn=1024",
|
||||
"--sysctl",
|
||||
@ -143,10 +135,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--pid",
|
||||
"host",
|
||||
"busybox",
|
||||
@ -166,10 +156,8 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--http-proxy=false",
|
||||
"--net",
|
||||
"",
|
||||
"--network-alias",
|
||||
"service_name",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"busybox",
|
||||
],
|
||||
)
|
||||
|
298
pytests/test_get_net_args.py
Normal file
298
pytests/test_get_net_args.py
Normal file
@ -0,0 +1,298 @@
|
||||
import unittest
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
from podman_compose import get_net_args
|
||||
|
||||
from .test_container_to_args import create_compose_mock
|
||||
|
||||
PROJECT_NAME = "test_project_name"
|
||||
SERVICE_NAME = "service_name"
|
||||
CONTAINER_NAME = f"{PROJECT_NAME}_{SERVICE_NAME}_1"
|
||||
|
||||
|
||||
def get_networked_compose(num_networks=1):
|
||||
compose = create_compose_mock(PROJECT_NAME)
|
||||
for network in range(num_networks):
|
||||
compose.networks[f"net{network}"] = {
|
||||
"driver": "bridge",
|
||||
"ipam": {
|
||||
"config": [
|
||||
{"subnet": f"192.168.{network}.0/24"},
|
||||
{"subnet": f"fd00:{network}::/64"},
|
||||
]
|
||||
},
|
||||
"enable_ipv6": True,
|
||||
}
|
||||
|
||||
return compose
|
||||
|
||||
|
||||
def get_minimal_container():
|
||||
return {
|
||||
"name": CONTAINER_NAME,
|
||||
"service_name": SERVICE_NAME,
|
||||
"image": "busybox",
|
||||
}
|
||||
|
||||
|
||||
class TestGetNetArgs(unittest.TestCase):
|
||||
def test_minimal(self):
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
|
||||
expected_args = [
|
||||
"--network=bridge",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_one_net(self):
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {}}
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_alias(self):
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {}}
|
||||
container["_aliases"] = ["alias1", "alias2"]
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
"--network-alias=alias1",
|
||||
"--network-alias=alias2",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_one_ipv4(self):
|
||||
ip = "192.168.0.42"
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {"ipv4_address": ip}}
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--ip={ip}",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertEqual(expected_args, args)
|
||||
|
||||
def test_one_ipv6(self):
|
||||
ipv6_address = "fd00:0::42"
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {"ipv6_address": ipv6_address}}
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--ip6={ipv6_address}",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_one_mac(self):
|
||||
mac = "00:11:22:33:44:55"
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {}}
|
||||
container["mac_address"] = mac
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--mac-address={mac}",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_one_mac_two_nets(self):
|
||||
mac = "00:11:22:33:44:55"
|
||||
compose = get_networked_compose(num_networks=6)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {}, "net1": {}}
|
||||
container["mac_address"] = mac
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0:mac={mac}",
|
||||
f"--network={PROJECT_NAME}_net1",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_two_nets_as_dict(self):
|
||||
compose = get_networked_compose(num_networks=2)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {"net0": {}, "net1": {}}
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--network={PROJECT_NAME}_net1",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_two_nets_as_list(self):
|
||||
compose = get_networked_compose(num_networks=2)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = ["net0", "net1"]
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0",
|
||||
f"--network={PROJECT_NAME}_net1",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_two_ipv4(self):
|
||||
ip0 = "192.168.0.42"
|
||||
ip1 = "192.168.1.42"
|
||||
compose = get_networked_compose(num_networks=2)
|
||||
container = get_minimal_container()
|
||||
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}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_two_ipv6(self):
|
||||
ip0 = "fd00:0::42"
|
||||
ip1 = "fd00:1::42"
|
||||
compose = get_networked_compose(num_networks=2)
|
||||
container = get_minimal_container()
|
||||
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}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
# custom extension; not supported by docker-compose
|
||||
def test_two_mac(self):
|
||||
mac0 = "00:00:00:00:00:01"
|
||||
mac1 = "00:00:00:00:00:02"
|
||||
compose = get_networked_compose(num_networks=2)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {
|
||||
"net0": {"podman.mac_address": mac0},
|
||||
"net1": {"podman.mac_address": mac1},
|
||||
}
|
||||
|
||||
expected_args = [
|
||||
f"--network={PROJECT_NAME}_net0:mac={mac0}",
|
||||
f"--network={PROJECT_NAME}_net1:mac={mac1}",
|
||||
f"--network-alias={SERVICE_NAME}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_mixed_mac(self):
|
||||
ip4_0 = "192.168.0.42"
|
||||
ip4_1 = "192.168.1.42"
|
||||
ip4_2 = "192.168.2.42"
|
||||
mac_0 = "00:00:00:00:00:01"
|
||||
mac_1 = "00:00:00:00:00:02"
|
||||
|
||||
compose = get_networked_compose(num_networks=3)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {
|
||||
"net0": {"ipv4_address": ip4_0},
|
||||
"net1": {"ipv4_address": ip4_1, "podman.mac_address": mac_0},
|
||||
"net2": {"ipv4_address": ip4_2},
|
||||
}
|
||||
container["mac_address"] = mac_1
|
||||
|
||||
expected_exception = (
|
||||
r"specifying mac_address on both container and network level " r"is not supported"
|
||||
)
|
||||
self.assertRaisesRegex(RuntimeError, expected_exception, get_net_args, compose, container)
|
||||
|
||||
def test_mixed_config(self):
|
||||
ip4_0 = "192.168.0.42"
|
||||
ip4_1 = "192.168.1.42"
|
||||
ip6_0 = "fd00:0::42"
|
||||
ip6_2 = "fd00:2::42"
|
||||
mac = "00:11:22:33:44:55"
|
||||
compose = get_networked_compose(num_networks=4)
|
||||
container = get_minimal_container()
|
||||
container["networks"] = {
|
||||
"net0": {"ipv4_address": ip4_0, "ipv6_address": ip6_0},
|
||||
"net1": {"ipv4_address": ip4_1},
|
||||
"net2": {"ipv6_address": ip6_2},
|
||||
"net3": {},
|
||||
}
|
||||
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}",
|
||||
]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
@parameterized.expand([
|
||||
("bridge", ["--network=bridge", f"--network-alias={SERVICE_NAME}"]),
|
||||
("host", ["--network=host"]),
|
||||
("none", []),
|
||||
("slirp4netns", ["--network=slirp4netns"]),
|
||||
("slirp4netns:cidr=10.42.0.0/24", ["--network=slirp4netns:cidr=10.42.0.0/24"]),
|
||||
("private", ["--network=private"]),
|
||||
("pasta", ["--network=pasta"]),
|
||||
("pasta:--ipv4-only,-a,10.0.2.0", ["--network=pasta:--ipv4-only,-a,10.0.2.0"]),
|
||||
("ns:my_namespace", ["--network=ns:my_namespace"]),
|
||||
("container:my_container", ["--network=container:my_container"]),
|
||||
])
|
||||
def test_network_modes(self, network_mode, expected_args):
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["network_mode"] = network_mode
|
||||
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
||||
|
||||
def test_network_mode_invalid(self):
|
||||
compose = get_networked_compose()
|
||||
container = get_minimal_container()
|
||||
container["network_mode"] = "invalid_mode"
|
||||
|
||||
with self.assertRaises(SystemExit):
|
||||
get_net_args(compose, container)
|
||||
|
||||
def test_network__mode_service(self):
|
||||
compose = get_networked_compose()
|
||||
compose.container_names_by_service = {
|
||||
"service_1": ["container_1"],
|
||||
"service_2": ["container_2"],
|
||||
}
|
||||
|
||||
container = get_minimal_container()
|
||||
container["network_mode"] = "service:service_2"
|
||||
|
||||
expected_args = ["--network=container:container_2"]
|
||||
args = get_net_args(compose, container)
|
||||
self.assertListEqual(expected_args, args)
|
61
tests/nets_test_ip/docker-compose.yml
Normal file
61
tests/nets_test_ip/docker-compose.yml
Normal file
@ -0,0 +1,61 @@
|
||||
version: "3"
|
||||
networks:
|
||||
shared-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: "172.19.1.0/24"
|
||||
internal-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: "172.19.2.0/24"
|
||||
|
||||
services:
|
||||
web1:
|
||||
image: busybox
|
||||
hostname: web1
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
||||
working_dir: /var/www/html
|
||||
networks:
|
||||
shared-network:
|
||||
ipv4_address: "172.19.1.10"
|
||||
podman.mac_address: "02:01:01:00:01:01"
|
||||
internal-network:
|
||||
ipv4_address: "172.19.2.10"
|
||||
podman.mac_address: "02:01:01:00:02:01"
|
||||
volumes:
|
||||
- ./test1.txt:/var/www/html/index.txt:ro,z
|
||||
web2:
|
||||
image: busybox
|
||||
hostname: web2
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
||||
working_dir: /var/www/html
|
||||
mac_address: "02:01:01:00:02:02"
|
||||
networks:
|
||||
internal-network:
|
||||
ipv4_address: "172.19.2.11"
|
||||
volumes:
|
||||
- ./test2.txt:/var/www/html/index.txt:ro,z
|
||||
|
||||
web3:
|
||||
image: busybox
|
||||
hostname: web2
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
||||
working_dir: /var/www/html
|
||||
networks:
|
||||
internal-network:
|
||||
volumes:
|
||||
- ./test3.txt:/var/www/html/index.txt:ro,z
|
||||
|
||||
web4:
|
||||
image: busybox
|
||||
hostname: web2
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
||||
working_dir: /var/www/html
|
||||
networks:
|
||||
internal-network:
|
||||
shared-network:
|
||||
ipv4_address: "172.19.1.13"
|
||||
volumes:
|
||||
- ./test4.txt:/var/www/html/index.txt:ro,z
|
1
tests/nets_test_ip/test1.txt
Normal file
1
tests/nets_test_ip/test1.txt
Normal file
@ -0,0 +1 @@
|
||||
test1
|
1
tests/nets_test_ip/test2.txt
Normal file
1
tests/nets_test_ip/test2.txt
Normal file
@ -0,0 +1 @@
|
||||
test2
|
1
tests/nets_test_ip/test3.txt
Normal file
1
tests/nets_test_ip/test3.txt
Normal file
@ -0,0 +1 @@
|
||||
test3
|
1
tests/nets_test_ip/test4.txt
Normal file
1
tests/nets_test_ip/test4.txt
Normal file
@ -0,0 +1 @@
|
||||
test4
|
116
tests/test_podman_compose_networks.py
Normal file
116
tests/test_podman_compose_networks.py
Normal file
@ -0,0 +1,116 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
"""
|
||||
test_podman_compose_networks.py
|
||||
|
||||
Tests the podman networking parameters
|
||||
"""
|
||||
|
||||
# pylint: disable=redefined-outer-name
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from .test_podman_compose import podman_compose_path
|
||||
from .test_podman_compose import test_path
|
||||
from .test_utils import RunSubprocessMixin
|
||||
|
||||
|
||||
class TestPodmanComposeNetwork(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(), "nets_test_ip", "docker-compose.yml")
|
||||
|
||||
def teardown(self):
|
||||
"""
|
||||
Ensures that the services within the "profile compose file" are removed between
|
||||
each test case.
|
||||
"""
|
||||
# run the test case
|
||||
yield
|
||||
|
||||
down_cmd = [
|
||||
"coverage",
|
||||
"run",
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
self.compose_file(),
|
||||
"kill",
|
||||
"-a",
|
||||
]
|
||||
self.run_subprocess(down_cmd)
|
||||
|
||||
def test_networks(self):
|
||||
up_cmd = [
|
||||
"coverage",
|
||||
"run",
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
self.compose_file(),
|
||||
"up",
|
||||
"-d",
|
||||
"--force-recreate",
|
||||
]
|
||||
|
||||
self.run_subprocess_assert_returncode(up_cmd)
|
||||
|
||||
check_cmd = [
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
self.compose_file(),
|
||||
"ps",
|
||||
"--format",
|
||||
'"{{.Names}}"',
|
||||
]
|
||||
out, _ = self.run_subprocess_assert_returncode(check_cmd)
|
||||
self.assertIn(b"nets_test_ip_web1_1", out)
|
||||
self.assertIn(b"nets_test_ip_web2_1", out)
|
||||
|
||||
expected_wget = {
|
||||
"172.19.1.10": "test1",
|
||||
"172.19.2.10": "test1",
|
||||
"172.19.2.11": "test2",
|
||||
"web3": "test3",
|
||||
"172.19.1.13": "test4",
|
||||
}
|
||||
|
||||
for service in ("web1", "web2"):
|
||||
for ip, expect in expected_wget.items():
|
||||
wget_cmd = [
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
self.compose_file(),
|
||||
"exec",
|
||||
service,
|
||||
"wget",
|
||||
"-q",
|
||||
"-O-",
|
||||
f"http://{ip}:8001/index.txt",
|
||||
]
|
||||
out, _ = self.run_subprocess_assert_returncode(wget_cmd)
|
||||
self.assertEqual(f"{expect}\r\n", out.decode('utf-8'))
|
||||
|
||||
expected_macip = {
|
||||
"web1": {
|
||||
"eth0": ["172.19.1.10", "02:01:01:00:01:01"],
|
||||
"eth1": ["172.19.2.10", "02:01:01:00:02:01"],
|
||||
},
|
||||
"web2": {"eth0": ["172.19.2.11", "02:01:01:00:02:02"]},
|
||||
}
|
||||
|
||||
for service, interfaces in expected_macip.items():
|
||||
ip_cmd = [
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
self.compose_file(),
|
||||
"exec",
|
||||
service,
|
||||
"ip",
|
||||
"addr",
|
||||
"show",
|
||||
]
|
||||
out, _ = self.run_subprocess_assert_returncode(ip_cmd)
|
||||
for interface, values in interfaces.items():
|
||||
ip, mac = values
|
||||
self.assertIn(f"ether {mac}", out.decode('utf-8'))
|
||||
self.assertIn(f"inet {ip}/", out.decode('utf-8'))
|
Loading…
Reference in New Issue
Block a user