diff --git a/podman/domain/containers_create.py b/podman/domain/containers_create.py index 867406a4..b6b1fadd 100644 --- a/podman/domain/containers_create.py +++ b/podman/domain/containers_create.py @@ -16,6 +16,8 @@ logger = logging.getLogger("podman.containers") +NAMED_VOLUME_PATTERN = re.compile(r'[a-zA-Z0-9][a-zA-Z0-9_.-]*') + class CreateMixin: # pylint: disable=too-few-public-methods """Class providing create method for ContainersManager.""" @@ -683,8 +685,21 @@ def parse_host_port(_container_port, _protocol, _host): raise ValueError("'mode' value should be a str") options.append(mode) - volume = {"Name": key, "Dest": value["bind"], "Options": options} - params["volumes"].append(volume) + # The Podman API only supports named volumes through the ``volume`` parameter. Directory + # mounting needs to happen through the ``mounts`` parameter. Luckily the translation + # isn't too complicated so we can just do it for the user if we suspect that the key + # isn't a named volume. + if NAMED_VOLUME_PATTERN.match(key): + volume = {"Name": key, "Dest": value["bind"], "Options": options} + params["volumes"].append(volume) + else: + mount_point = { + "destination": value['bind'], + "options": options, + "source": key, + "type": 'bind', + } + params["mounts"].append(mount_point) for item in args.pop("secrets", []): if isinstance(item, Secret): diff --git a/podman/tests/integration/test_container_create.py b/podman/tests/integration/test_container_create.py index eb5c6f35..45f2f202 100644 --- a/podman/tests/integration/test_container_create.py +++ b/podman/tests/integration/test_container_create.py @@ -24,7 +24,7 @@ def tearUp(self): for container in self.containers: container.remove(force=True) - def test_container_volume_mount(self): + def test_container_named_volume_mount(self): with self.subTest("Check volume mount"): volumes = { 'test_bind_1': {'bind': '/mnt/vol1', 'mode': 'rw'}, @@ -52,6 +52,33 @@ def test_container_volume_mount(self): for o in other_options: self.assertIn(o, mount.get('Options')) + def test_container_directory_volume_mount(self): + """Test that directories can be mounted with the ``volume`` parameter.""" + with self.subTest("Check bind mount"): + volumes = { + "/etc/hosts": dict(bind="/test_ro", mode='ro'), + "/etc/hosts": dict(bind="/test_rw", mode='rw'), + } + container = self.client.containers.create( + self.alpine_image, command=["cat", "/test_ro", "/test_rw"], volumes=volumes + ) + container_mounts = container.attrs.get('Mounts', {}) + self.assertEqual(len(container_mounts), len(volumes)) + + self.containers.append(container) + + for directory, mount_spec in volumes.items(): + self.assertIn( + f"{directory}:{mount_spec['bind']}:{mount_spec['mode']},rprivate,rbind", + container.attrs.get('HostConfig', {}).get('Binds', list()), + ) + + # check if container can be started and exits with EC == 0 + container.start() + container.wait() + + self.assertEqual(container.attrs.get('State', dict()).get('ExitCode', 256), 0) + def test_container_extra_hosts(self): """Test Container Extra hosts""" extra_hosts = {"host1 host3": "127.0.0.2", "host2": "127.0.0.3"}