mirror of
https://github.com/containers/podman-compose.git
synced 2025-05-01 21:04:49 +02:00
Adding basic support for --profile argument
Signed-off-by: Dixon Whitmire <dixonwh@gmail.com>
This commit is contained in:
parent
bc5f0123d9
commit
8d8df0bc28
@ -1523,6 +1523,8 @@ class PodmanCompose:
|
|||||||
# log(filename, json.dumps(content, indent = 2))
|
# log(filename, json.dumps(content, indent = 2))
|
||||||
content = rec_subs(content, self.environ)
|
content = rec_subs(content, self.environ)
|
||||||
rec_merge(compose, content)
|
rec_merge(compose, content)
|
||||||
|
resolved_services = self._resolve_profiles(compose.get("services", {}), set(args.profile))
|
||||||
|
compose["services"] = resolved_services
|
||||||
self.merged_yaml = yaml.safe_dump(compose)
|
self.merged_yaml = yaml.safe_dump(compose)
|
||||||
merged_json_b = json.dumps(compose, separators=(",", ":")).encode("utf-8")
|
merged_json_b = json.dumps(compose, separators=(",", ":")).encode("utf-8")
|
||||||
self.yaml_hash = hashlib.sha256(merged_json_b).hexdigest()
|
self.yaml_hash = hashlib.sha256(merged_json_b).hexdigest()
|
||||||
@ -1552,6 +1554,8 @@ class PodmanCompose:
|
|||||||
if services is None:
|
if services is None:
|
||||||
services = {}
|
services = {}
|
||||||
log("WARNING: No services defined")
|
log("WARNING: No services defined")
|
||||||
|
# include services with no profile defined or the selected profiles
|
||||||
|
services = self._resolve_profiles(services, set(args.profile))
|
||||||
|
|
||||||
# NOTE: maybe add "extends.service" to _deps at this stage
|
# NOTE: maybe add "extends.service" to _deps at this stage
|
||||||
flat_deps(services, with_extends=True)
|
flat_deps(services, with_extends=True)
|
||||||
@ -1666,6 +1670,28 @@ class PodmanCompose:
|
|||||||
self.containers = containers
|
self.containers = containers
|
||||||
self.container_by_name = {c["name"]: c for c in containers}
|
self.container_by_name = {c["name"]: c for c in containers}
|
||||||
|
|
||||||
|
def _resolve_profiles(self, defined_services, requested_profiles=None):
|
||||||
|
"""
|
||||||
|
Returns a service dictionary (key = service name, value = service config) compatible with the requested_profiles
|
||||||
|
list.
|
||||||
|
|
||||||
|
The returned service dictionary contains all services which do not include/reference a profile in addition to
|
||||||
|
services that match the requested_profiles.
|
||||||
|
|
||||||
|
:param defined_services: The service dictionary
|
||||||
|
:param requested_profiles: The profiles requested using the --profile arg.
|
||||||
|
"""
|
||||||
|
if requested_profiles is None:
|
||||||
|
requested_profiles = set()
|
||||||
|
|
||||||
|
services = {}
|
||||||
|
|
||||||
|
for name, config in defined_services.items():
|
||||||
|
service_profiles = set(config.get("profiles", []))
|
||||||
|
if not service_profiles or requested_profiles.intersection(service_profiles):
|
||||||
|
services[name] = config
|
||||||
|
return services
|
||||||
|
|
||||||
def _parse_args(self):
|
def _parse_args(self):
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
|
||||||
self._init_global_parser(parser)
|
self._init_global_parser(parser)
|
||||||
@ -1717,6 +1743,13 @@ class PodmanCompose:
|
|||||||
action="append",
|
action="append",
|
||||||
default=[],
|
default=[],
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--profile",
|
||||||
|
help="Specify a profile to enable",
|
||||||
|
metavar="profile",
|
||||||
|
action="append",
|
||||||
|
default=[]
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p",
|
"-p",
|
||||||
"--project-name",
|
"--project-name",
|
||||||
|
25
tests/conftest.py
Normal file
25
tests/conftest.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""conftest.py
|
||||||
|
|
||||||
|
Defines global pytest fixtures available to all tests.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def base_path():
|
||||||
|
"""Returns the base path for the project"""
|
||||||
|
return Path(__file__).parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_path(base_path):
|
||||||
|
"""Returns the path to the tests directory"""
|
||||||
|
return os.path.join(base_path, "tests")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def podman_compose_path(base_path):
|
||||||
|
"""Returns the path to the podman compose script"""
|
||||||
|
return os.path.join(base_path, "podman_compose.py")
|
24
tests/profile/docker-compose.yml
Normal file
24
tests/profile/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
default-service:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
service-1:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
profiles:
|
||||||
|
- profile-1
|
||||||
|
service-2:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
profiles:
|
||||||
|
- profile-2
|
78
tests/test_podman_compose_config.py
Normal file
78
tests/test_podman_compose_config.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
test_podman_compose_config.py
|
||||||
|
|
||||||
|
Tests the podman-compose config command which is used to return defined compose services.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
from test_podman_compose import capture
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def profile_compose_file(test_path):
|
||||||
|
""""Returns the path to the `profile` compose file used for this test module"""
|
||||||
|
return os.path.join(test_path, "profile", "docker-compose.yml")
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_no_profiles(podman_compose_path, profile_compose_file):
|
||||||
|
"""
|
||||||
|
Tests podman-compose config command without profile enablement.
|
||||||
|
|
||||||
|
:param podman_compose_path: The fixture used to specify the path to the podman compose file.
|
||||||
|
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test.
|
||||||
|
"""
|
||||||
|
config_cmd = [
|
||||||
|
"python3",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
profile_compose_file,
|
||||||
|
"config"
|
||||||
|
]
|
||||||
|
|
||||||
|
out, err, return_code = capture(config_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
string_output = out.decode("utf-8")
|
||||||
|
assert "default-service" in string_output
|
||||||
|
assert "service-1" not in string_output
|
||||||
|
assert "service-2" not in string_output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("profiles, expected_services",
|
||||||
|
[
|
||||||
|
(["--profile", "profile-1", "config"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": False}),
|
||||||
|
(["--profile", "profile-2", "config"],
|
||||||
|
{"default-service": True, "service-1": False, "service-2": True}),
|
||||||
|
(["--profile", "profile-1", "--profile", "profile-2", "config"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": True})
|
||||||
|
])
|
||||||
|
def test_config_profiles(podman_compose_path, profile_compose_file, profiles, expected_services):
|
||||||
|
"""
|
||||||
|
Tests podman-compose
|
||||||
|
:param podman_compose_path: The fixture used to specify the path to the podman compose file.
|
||||||
|
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test.
|
||||||
|
:param profiles: The enabled profiles for the parameterized test.
|
||||||
|
:param expected_services: Dictionary used to model the expected "enabled" services in the profile.
|
||||||
|
Key = service name, Value = True if the service is enabled, otherwise False.
|
||||||
|
"""
|
||||||
|
config_cmd = [
|
||||||
|
"python3",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
profile_compose_file
|
||||||
|
]
|
||||||
|
config_cmd.extend(profiles)
|
||||||
|
|
||||||
|
out, err, return_code = capture(config_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
actual_output = out.decode("utf-8")
|
||||||
|
|
||||||
|
assert len(expected_services) == 3
|
||||||
|
|
||||||
|
actual_services = {}
|
||||||
|
for service, expected_check in expected_services.items():
|
||||||
|
actual_services[service] = service in actual_output
|
||||||
|
|
||||||
|
assert expected_services == actual_services
|
80
tests/test_podman_compose_up_down.py
Normal file
80
tests/test_podman_compose_up_down.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
test_podman_compose_up_down.py
|
||||||
|
|
||||||
|
Tests the podman compose up and down commands used to create and remove services.
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
from test_podman_compose import capture
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def profile_compose_file(test_path):
|
||||||
|
""""Returns the path to the `profile` compose file used for this test module"""
|
||||||
|
return os.path.join(test_path, "profile", "docker-compose.yml")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def teardown(podman_compose_path, profile_compose_file):
|
||||||
|
"""
|
||||||
|
Ensures that the services within the "profile compose file" are removed between each test case.
|
||||||
|
|
||||||
|
:param podman_compose_path: The path to the podman compose script.
|
||||||
|
:param profile_compose_file: The path to the compose file used for this test module.
|
||||||
|
"""
|
||||||
|
# run the test case
|
||||||
|
yield
|
||||||
|
|
||||||
|
down_cmd = [
|
||||||
|
"python3",
|
||||||
|
podman_compose_path,
|
||||||
|
"--profile",
|
||||||
|
"profile-1",
|
||||||
|
"--profile",
|
||||||
|
"profile-2",
|
||||||
|
"-f",
|
||||||
|
profile_compose_file,
|
||||||
|
"down"
|
||||||
|
]
|
||||||
|
capture(down_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("profiles, expected_services",
|
||||||
|
[
|
||||||
|
(["--profile", "profile-1", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": False}),
|
||||||
|
(["--profile", "profile-2", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": False, "service-2": True}),
|
||||||
|
(["--profile", "profile-1", "--profile", "profile-2", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": True})
|
||||||
|
])
|
||||||
|
def test_up(podman_compose_path, profile_compose_file, profiles, expected_services):
|
||||||
|
up_cmd = [
|
||||||
|
"python3",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
profile_compose_file,
|
||||||
|
]
|
||||||
|
up_cmd.extend(profiles)
|
||||||
|
|
||||||
|
out, err, return_code = capture(up_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
check_cmd = [
|
||||||
|
"podman",
|
||||||
|
"container",
|
||||||
|
"ps",
|
||||||
|
"--format",
|
||||||
|
'"{{.Names}}"',
|
||||||
|
]
|
||||||
|
out, err, return_code = capture(check_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
assert len(expected_services) == 3
|
||||||
|
actual_output = out.decode("utf-8")
|
||||||
|
|
||||||
|
actual_services = {}
|
||||||
|
for service, expected_check in expected_services.items():
|
||||||
|
actual_services[service] = service in actual_output
|
||||||
|
|
||||||
|
assert expected_services == actual_services
|
Loading…
Reference in New Issue
Block a user