mirror of
https://github.com/containers/podman-compose.git
synced 2025-08-10 22:24:16 +02:00
Merge pull request #1279 from mokibit/add-volume-bind-create-host-path-option
Add volume bind `create_host_path` option
This commit is contained in:
@ -0,0 +1 @@
|
||||
Implemented volumes bind `create_host_path` option.
|
@ -405,6 +405,12 @@ async def assert_volume(compose: PodmanCompose, mount_dict: dict[str, Any]) -> N
|
||||
mount_src = mount_dict["source"]
|
||||
mount_src = os.path.realpath(os.path.join(basedir, os.path.expanduser(mount_src)))
|
||||
if not os.path.exists(mount_src):
|
||||
bind_opts = mount_dict.get("bind", {})
|
||||
if "create_host_path" in bind_opts and not bind_opts["create_host_path"]:
|
||||
raise ValueError(
|
||||
"invalid mount config for type 'bind': bind source path does not exist: "
|
||||
f"{mount_src}"
|
||||
)
|
||||
try:
|
||||
os.makedirs(mount_src, exist_ok=True)
|
||||
except OSError:
|
||||
|
0
tests/integration/vol/long_syntax/__init__.py
Normal file
0
tests/integration/vol/long_syntax/__init__.py
Normal file
27
tests/integration/vol/long_syntax/docker-compose.yml
Normal file
27
tests/integration/vol/long_syntax/docker-compose.yml
Normal file
@ -0,0 +1,27 @@
|
||||
version: "3"
|
||||
services:
|
||||
create_host_path_default_true: # default is to always create
|
||||
image: nopush/podman-compose-test
|
||||
command: ["/bin/busybox", "mkdir", "cont/test_dir/new_dir"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./test_dir
|
||||
target: /cont/test_dir
|
||||
create_host_path_true:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["/bin/busybox", "mkdir", "cont/test_dir/new_dir"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./test_dir
|
||||
target: /cont/test_dir
|
||||
bind:
|
||||
create_host_path: true
|
||||
create_host_path_false:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["/bin/busybox", "mkdir", "cont/test_dir/new_dir"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./test_dir
|
||||
target: /cont/test_dir
|
||||
bind:
|
||||
create_host_path: false
|
@ -0,0 +1,88 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path(test_ref_folder: str) -> str:
|
||||
return os.path.join(test_path(), "vol", test_ref_folder, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeVolLongSyntax(unittest.TestCase, RunSubprocessMixin):
|
||||
@parameterized.expand([
|
||||
(True, "create_host_path_default_true"),
|
||||
(True, "create_host_path_true"),
|
||||
(True, "create_host_path_false"),
|
||||
(False, "create_host_path_default_true"),
|
||||
(False, "create_host_path_true"),
|
||||
])
|
||||
def test_source_host_dir(self, source_dir_exists: bool, service_name: str) -> None:
|
||||
project_dir = os.path.join(test_path(), "vol", "long_syntax")
|
||||
if source_dir_exists:
|
||||
# create host source directory for volume
|
||||
os.mkdir(os.path.join(project_dir, "test_dir"))
|
||||
else:
|
||||
# make sure there is no such directory
|
||||
self.assertFalse(os.path.isdir(os.path.join(project_dir, "test_dir")))
|
||||
|
||||
try:
|
||||
self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("long_syntax"),
|
||||
"up",
|
||||
"-d",
|
||||
f"{service_name}",
|
||||
],
|
||||
0,
|
||||
)
|
||||
# command of the service creates a new directory on mounted directory 'test_dir' in
|
||||
# the container. Check if host directory now has the same directory. It represents a
|
||||
# successful mount.
|
||||
# If source host directory does not exist, it is created
|
||||
self.assertTrue(os.path.isdir(os.path.join(project_dir, "test_dir/new_dir")))
|
||||
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("long_syntax"),
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
||||
shutil.rmtree(os.path.join(project_dir, "test_dir"))
|
||||
|
||||
def test_no_host_source_dir_create_host_path_false(self) -> None:
|
||||
try:
|
||||
_, error = self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("long_syntax"),
|
||||
"up",
|
||||
"-d",
|
||||
"create_host_path_false",
|
||||
],
|
||||
1,
|
||||
)
|
||||
self.assertIn(
|
||||
b"invalid mount config for type 'bind': bind source path does not exist:", error
|
||||
)
|
||||
finally:
|
||||
self.run_subprocess([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("long_syntax"),
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
0
tests/integration/vol/short_syntax/__init__.py
Normal file
0
tests/integration/vol/short_syntax/__init__.py
Normal file
13
tests/integration/vol/short_syntax/docker-compose.yml
Normal file
13
tests/integration/vol/short_syntax/docker-compose.yml
Normal file
@ -0,0 +1,13 @@
|
||||
version: "3"
|
||||
services:
|
||||
test1:
|
||||
image: nopush/podman-compose-test
|
||||
#["/bin/busybox", "sh", "-c", "env | grep ZZVAR3"]
|
||||
command: ["/bin/busybox", "mkdir", "cont/test_dir/new_dir"]
|
||||
volumes:
|
||||
- ./test_dir:/cont/test_dir
|
||||
test2:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["/bin/busybox", "mkdir", "cont/test_dir/new_dir"]
|
||||
volumes:
|
||||
- ./test_dir:/cont/test_dir
|
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path(test_ref_folder: str) -> str:
|
||||
return os.path.join(test_path(), "vol", test_ref_folder, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeVolShortSyntax(unittest.TestCase, RunSubprocessMixin):
|
||||
@parameterized.expand([
|
||||
(True, "test1"),
|
||||
(False, "test2"),
|
||||
])
|
||||
def test_source_host_dir(self, source_host_dir_exists: bool, service_name: str) -> None:
|
||||
project_dir = os.path.join(test_path(), "vol", "short_syntax")
|
||||
# create host source directory for volume
|
||||
if source_host_dir_exists:
|
||||
os.mkdir(os.path.join(project_dir, "test_dir"))
|
||||
try:
|
||||
self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("short_syntax"),
|
||||
"up",
|
||||
"-d",
|
||||
f"{service_name}",
|
||||
],
|
||||
0,
|
||||
)
|
||||
# command of the service creates a new directory on mounted directory 'test_dir' in
|
||||
# container. Check if host directory now has the same directory. It represents a
|
||||
# successful mount.
|
||||
# On service test2 source host directory is created as it did not exist
|
||||
self.assertTrue(os.path.isdir(os.path.join(project_dir, "test_dir/new_dir")))
|
||||
|
||||
finally:
|
||||
self.run_subprocess([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path("short_syntax"),
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
||||
shutil.rmtree(os.path.join(project_dir, "test_dir"))
|
@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
from typing import Any
|
||||
from unittest import mock
|
||||
@ -679,6 +680,71 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
||||
],
|
||||
)
|
||||
|
||||
@parameterized.expand([
|
||||
(
|
||||
"create_host_path_set_to_true",
|
||||
{"bind": {"create_host_path": True}},
|
||||
),
|
||||
(
|
||||
"create_host_path_default_true",
|
||||
{},
|
||||
),
|
||||
])
|
||||
async def test_volumes_bind_mount_create_source_dir(self, test_name: str, bind: dict) -> None:
|
||||
# creates a missing source dir
|
||||
c = create_compose_mock()
|
||||
c.prefer_volume_over_mount = True
|
||||
cnt = get_minimal_container()
|
||||
|
||||
cnt["_service"] = cnt["service_name"]
|
||||
|
||||
volume_info = {
|
||||
"type": "bind",
|
||||
"source": "./not_exists/foo",
|
||||
"target": "/mnt",
|
||||
}
|
||||
volume_info.update(bind)
|
||||
cnt["volumes"] = [
|
||||
volume_info,
|
||||
]
|
||||
|
||||
args = await container_to_args(c, cnt)
|
||||
|
||||
self.assertEqual(
|
||||
args,
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"-v",
|
||||
f"{get_test_file_path('./test_dirname/not_exists/foo')}:/mnt",
|
||||
"--network=bridge:alias=service_name",
|
||||
"busybox",
|
||||
],
|
||||
)
|
||||
dir_path = get_test_file_path('./test_dirname/not_exists/foo')
|
||||
shutil.rmtree(dir_path)
|
||||
|
||||
# throws an error as the source path does not exist and its creation was suppressed with the
|
||||
# create_host_path = False option
|
||||
async def test_volumes_bind_mount_source_does_not_exist(self) -> None:
|
||||
c = create_compose_mock()
|
||||
c.prefer_volume_over_mount = True
|
||||
cnt = get_minimal_container()
|
||||
|
||||
cnt["_service"] = cnt["service_name"]
|
||||
|
||||
cnt["volumes"] = [
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "./not_exists/foo",
|
||||
"target": "/mnt",
|
||||
"bind": {"create_host_path": False},
|
||||
}
|
||||
]
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
await container_to_args(c, cnt)
|
||||
|
||||
@parameterized.expand([
|
||||
("not_compat", False, "test_project_name", "test_project_name_network1"),
|
||||
("compat_no_dash", True, "test_project_name", "test_project_name_network1"),
|
||||
|
Reference in New Issue
Block a user