mirror of
https://github.com/containers/podman-compose.git
synced 2024-12-27 09:09:05 +01:00
Support podman-specific per-network mac_address specifiation
Signed-off-by: Bas Zoetekouw <bas.zoetekouw@surf.nl>
This commit is contained in:
parent
91fbea3d89
commit
45ca1f994f
47
docs/Extensions.md
Normal file
47
docs/Extensions.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 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"
|
||||||
|
```
|
@ -779,10 +779,8 @@ async def assert_cnt_nets(compose, cnt):
|
|||||||
def get_net_args(compose, cnt):
|
def get_net_args(compose, cnt):
|
||||||
service_name = cnt["service_name"]
|
service_name = cnt["service_name"]
|
||||||
net_args = []
|
net_args = []
|
||||||
mac_address = cnt.get("mac_address", None)
|
|
||||||
if mac_address:
|
|
||||||
net_args.extend(["--mac-address", mac_address])
|
|
||||||
is_bridge = False
|
is_bridge = False
|
||||||
|
mac_address = cnt.get("mac_address", None)
|
||||||
net = cnt.get("network_mode", None)
|
net = cnt.get("network_mode", None)
|
||||||
if net:
|
if net:
|
||||||
if net == "none":
|
if net == "none":
|
||||||
@ -866,6 +864,18 @@ def get_net_args(compose, cnt):
|
|||||||
else:
|
else:
|
||||||
multiple_nets = {net: net_config or {} for net, net_config in multiple_nets.items()}
|
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("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():
|
for net_, net_config_ in multiple_nets.items():
|
||||||
net_desc = nets[net_] or {}
|
net_desc = nets[net_] or {}
|
||||||
is_ext = net_desc.get("external", None)
|
is_ext = net_desc.get("external", None)
|
||||||
@ -875,6 +885,16 @@ def get_net_args(compose, cnt):
|
|||||||
|
|
||||||
ipv4 = net_config_.get("ipv4_address", None)
|
ipv4 = net_config_.get("ipv4_address", None)
|
||||||
ipv6 = net_config_.get("ipv6_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 = []
|
net_options = []
|
||||||
if ipv4:
|
if ipv4:
|
||||||
@ -893,6 +913,8 @@ def get_net_args(compose, cnt):
|
|||||||
net_args.append(f"--ip={ip}")
|
net_args.append(f"--ip={ip}")
|
||||||
if ip6:
|
if ip6:
|
||||||
net_args.append(f"--ip6={ip6}")
|
net_args.append(f"--ip6={ip6}")
|
||||||
|
if mac_address:
|
||||||
|
net_args.append(f"--mac-address={mac_address}")
|
||||||
|
|
||||||
if is_bridge:
|
if is_bridge:
|
||||||
net_args.extend(["--network-alias", ",".join(aliases)])
|
net_args.extend(["--network-alias", ",".join(aliases)])
|
||||||
|
@ -104,10 +104,27 @@ class TestGetNetArgs(unittest.TestCase):
|
|||||||
container["mac_address"] = mac
|
container["mac_address"] = mac
|
||||||
|
|
||||||
expected_args = [
|
expected_args = [
|
||||||
"--mac-address",
|
|
||||||
mac,
|
|
||||||
"--network",
|
"--network",
|
||||||
f"{PROJECT_NAME}_net0",
|
f"{PROJECT_NAME}_net0",
|
||||||
|
"--mac-address=" + mac,
|
||||||
|
"--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 = [
|
||||||
|
"--network",
|
||||||
|
f"{PROJECT_NAME}_net0:mac={mac}",
|
||||||
|
"--network",
|
||||||
|
f"{PROJECT_NAME}_net1",
|
||||||
"--network-alias",
|
"--network-alias",
|
||||||
SERVICE_NAME,
|
SERVICE_NAME,
|
||||||
]
|
]
|
||||||
@ -182,6 +199,49 @@ class TestGetNetArgs(unittest.TestCase):
|
|||||||
args = get_net_args(compose, container)
|
args = get_net_args(compose, container)
|
||||||
self.assertListEqual(expected_args, args)
|
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 = [
|
||||||
|
"--network",
|
||||||
|
f"{PROJECT_NAME}_net0:mac={mac0}",
|
||||||
|
"--network",
|
||||||
|
f"{PROJECT_NAME}_net1:mac={mac1}",
|
||||||
|
"--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):
|
def test_mixed_config(self):
|
||||||
ip4_0 = "192.168.0.42"
|
ip4_0 = "192.168.0.42"
|
||||||
ip4_1 = "192.168.1.42"
|
ip4_1 = "192.168.1.42"
|
||||||
@ -199,10 +259,8 @@ class TestGetNetArgs(unittest.TestCase):
|
|||||||
container["mac_address"] = mac
|
container["mac_address"] = mac
|
||||||
|
|
||||||
expected_args = [
|
expected_args = [
|
||||||
"--mac-address",
|
|
||||||
mac,
|
|
||||||
"--network",
|
"--network",
|
||||||
f"{PROJECT_NAME}_net0:ip={ip4_0},ip={ip6_0}",
|
f"{PROJECT_NAME}_net0:ip={ip4_0},ip={ip6_0},mac={mac}",
|
||||||
"--network",
|
"--network",
|
||||||
f"{PROJECT_NAME}_net1:ip={ip4_1}",
|
f"{PROJECT_NAME}_net1:ip={ip4_1}",
|
||||||
"--network",
|
"--network",
|
||||||
|
@ -20,8 +20,10 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
shared-network:
|
shared-network:
|
||||||
ipv4_address: "172.19.1.10"
|
ipv4_address: "172.19.1.10"
|
||||||
|
podman.mac_address: "02:01:01:00:01:01"
|
||||||
internal-network:
|
internal-network:
|
||||||
ipv4_address: "172.19.2.10"
|
ipv4_address: "172.19.2.10"
|
||||||
|
podman.mac_address: "02:01:01:00:02:01"
|
||||||
volumes:
|
volumes:
|
||||||
- ./test1.txt:/var/www/html/index.txt:ro,z
|
- ./test1.txt:/var/www/html/index.txt:ro,z
|
||||||
web2:
|
web2:
|
||||||
|
@ -91,8 +91,11 @@ class TestPodmanComposeNetwork(RunSubprocessMixin, unittest.TestCase):
|
|||||||
self.assertEqual(f"{expect}\r\n", out.decode('utf-8'))
|
self.assertEqual(f"{expect}\r\n", out.decode('utf-8'))
|
||||||
|
|
||||||
expected_macip = {
|
expected_macip = {
|
||||||
"web1": {"eth0": ["172.19.1.10"], "eth1": ["172.19.2.10"]},
|
"web1": {
|
||||||
"web2": {"eth0": ["172.19.2.11"]},
|
"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():
|
for service, interfaces in expected_macip.items():
|
||||||
@ -108,5 +111,6 @@ class TestPodmanComposeNetwork(RunSubprocessMixin, unittest.TestCase):
|
|||||||
]
|
]
|
||||||
out, _ = self.run_subprocess_assert_returncode(ip_cmd)
|
out, _ = self.run_subprocess_assert_returncode(ip_cmd)
|
||||||
for interface, values in interfaces.items():
|
for interface, values in interfaces.items():
|
||||||
ip = values[0]
|
ip, mac = values
|
||||||
|
self.assertIn(f"ether {mac}", out.decode('utf-8'))
|
||||||
self.assertIn(f"inet {ip}/", out.decode('utf-8'))
|
self.assertIn(f"inet {ip}/", out.decode('utf-8'))
|
||||||
|
Loading…
Reference in New Issue
Block a user