Skip to content

Commit 9cde399

Browse files
authored
Merge pull request #1238 from uosis/env-var-override
Add support for providing x-podman settings using environment variables
2 parents 3e579f6 + 04155d0 commit 9cde399

File tree

6 files changed

+120
-11
lines changed

6 files changed

+120
-11
lines changed

docs/Extensions.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ x-podman:
156156
By default `default_net_name_compat` is `false`. This will change to `true` at some point and the
157157
setting will be removed.
158158

159+
This setting can also be changed by setting `PODMAN_COMPOSE_DEFAULT_NET_NAME_COMPAT` environment
160+
variable.
161+
159162
## Compatibility of default network behavior between docker-compose and podman-compose
160163

161164
When there is no network defined (neither network-mode nor networks) in service,
@@ -176,6 +179,9 @@ x-podman:
176179
default_net_behavior_compat: true
177180
```
178181

182+
This setting can also be changed by setting `PODMAN_COMPOSE_DEFAULT_NET_BEHAVIOR_COMPAT` environment
183+
variable.
184+
179185
## Custom pods management
180186

181187
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.
@@ -195,6 +201,9 @@ x-podman:
195201
in_pod: false
196202
```
197203

204+
This setting can also be changed by setting `PODMAN_COMPOSE_IN_POD` environment
205+
variable.
206+
198207
It is also possible to override the default arguments for pod creation that are
199208
used when --pod-args is not passed on the command line:
200209
```yml
@@ -208,3 +217,6 @@ x-podman:
208217
```
209218
When not set in docker-compose.yml or on the command line, the pod args default
210219
to `["--infra=false", "--share="]`.
220+
221+
This setting can also be changed by setting `PODMAN_COMPOSE_POD_ARGS` environment
222+
variable.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add support for setting x-podman values using PODMAN_COMPOSE_* environment variables.

podman_compose.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,9 @@ def default_network_name_for_project(compose: PodmanCompose, net: str, is_ext: A
370370

371371
assert compose.project_name is not None
372372

373-
default_net_name_compat = compose.x_podman.get("default_net_name_compat", False)
373+
default_net_name_compat = compose.x_podman.get(
374+
PodmanCompose.XPodmanSettingKey.DEFAULT_NET_NAME_COMPAT, False
375+
)
374376
if default_net_name_compat is True:
375377
return f"{compose.project_name.replace('-', '')}_{net}"
376378
return f"{compose.project_name}_{net}"
@@ -1968,6 +1970,12 @@ def dotenv_to_dict(dotenv_path: str) -> dict[str, str | None]:
19681970

19691971

19701972
class PodmanCompose:
1973+
class XPodmanSettingKey(Enum):
1974+
DEFAULT_NET_NAME_COMPAT = "default_net_name_compat"
1975+
DEFAULT_NET_BEHAVIOR_COMPAT = "default_net_behavior_compat"
1976+
IN_POD = "in_pod"
1977+
POD_ARGS = "pod_args"
1978+
19711979
def __init__(self) -> None:
19721980
self.podman: Podman
19731981
self.podman_version: str | None = None
@@ -1988,7 +1996,7 @@ def __init__(self) -> None:
19881996
self.services: dict[str, Any]
19891997
self.all_services: set[Any] = set()
19901998
self.prefer_volume_over_mount = True
1991-
self.x_podman: dict[str, Any] = {}
1999+
self.x_podman: dict[PodmanCompose.XPodmanSettingKey, Any] = {}
19922000
self.merged_yaml: Any
19932001
self.yaml_hash = ""
19942002
self.console_colors = [
@@ -2059,7 +2067,7 @@ async def run(self, argv: list[str] | None = None) -> None:
20592067

20602068
def resolve_in_pod(self) -> bool:
20612069
if self.global_args.in_pod in (None, ''):
2062-
self.global_args.in_pod = self.x_podman.get("in_pod", "1")
2070+
self.global_args.in_pod = self.x_podman.get(PodmanCompose.XPodmanSettingKey.IN_POD, "1")
20632071
# otherwise use `in_pod` value provided by command line
20642072
return self.global_args.in_pod
20652073

@@ -2070,7 +2078,43 @@ def resolve_pod_args(self) -> list[str]:
20702078
# - Default value
20712079
if self.global_args.pod_args is not None:
20722080
return shlex.split(self.global_args.pod_args)
2073-
return self.x_podman.get("pod_args", ["--infra=false", "--share="])
2081+
return self.x_podman.get(
2082+
PodmanCompose.XPodmanSettingKey.POD_ARGS, ["--infra=false", "--share="]
2083+
)
2084+
2085+
def _parse_x_podman_settings(self, compose: dict[str, Any], environ: dict[str, str]) -> None:
2086+
known_keys = {s.value: s for s in PodmanCompose.XPodmanSettingKey}
2087+
2088+
self.x_podman = {}
2089+
2090+
for k, v in compose.get("x-podman", {}).items():
2091+
known_key = known_keys.get(k)
2092+
if known_key:
2093+
self.x_podman[known_key] = v
2094+
else:
2095+
log.warning(
2096+
"unknown x-podman key [%s] in compose file, supported keys: %s",
2097+
k,
2098+
", ".join(known_keys.keys()),
2099+
)
2100+
2101+
env = {
2102+
key.removeprefix("PODMAN_COMPOSE_").lower(): value
2103+
for key, value in environ.items()
2104+
if key.startswith("PODMAN_COMPOSE_")
2105+
and key not in {"PODMAN_COMPOSE_PROVIDER", "PODMAN_COMPOSE_WARNING_LOGS"}
2106+
}
2107+
2108+
for k, v in env.items():
2109+
known_key = known_keys.get(k)
2110+
if known_key:
2111+
self.x_podman[known_key] = v
2112+
else:
2113+
log.warning(
2114+
"unknown PODMAN_COMPOSE_ key [%s] in environment, supported keys: %s",
2115+
k,
2116+
", ".join(known_keys.keys()),
2117+
)
20742118

20752119
def _parse_compose_file(self) -> None:
20762120
args = self.global_args
@@ -2219,6 +2263,8 @@ def _parse_compose_file(self) -> None:
22192263
log.debug(" ** merged:\n%s", json.dumps(compose, indent=2))
22202264
# ver = compose.get('version')
22212265

2266+
self._parse_x_podman_settings(compose, self.environ)
2267+
22222268
services: dict | None = compose.get("services")
22232269
if services is None:
22242270
services = {}
@@ -2236,7 +2282,7 @@ def _parse_compose_file(self) -> None:
22362282
nets["default"] = None
22372283

22382284
self.networks = nets
2239-
if compose.get("x-podman", {}).get("default_net_behavior_compat", False):
2285+
if self.x_podman.get(PodmanCompose.XPodmanSettingKey.DEFAULT_NET_BEHAVIOR_COMPAT, False):
22402286
# If there is no network_mode and networks in service,
22412287
# docker-compose will create default network named '<project_name>_default'
22422288
# and add the service to the default network.
@@ -2353,8 +2399,6 @@ def _parse_compose_file(self) -> None:
23532399
given_containers.sort(key=lambda c: len(c.get("_deps", [])))
23542400
# log("sorted:", [c["name"] for c in given_containers])
23552401

2356-
self.x_podman = compose.get("x-podman", {})
2357-
23582402
args.in_pod = self.resolve_in_pod()
23592403
args.pod_arg_list = self.resolve_pod_args()
23602404
pods, containers = transform(args, project_name, given_containers)

tests/integration/in_pod/test_podman_compose_in_pod.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,57 @@ def test_x_podman_in_pod_not_exists_command_line_in_pod_false(self) -> None:
467467
# can not actually find this pod because it was not created
468468
self.run_subprocess_assert_returncode(command_rm_pod, 1)
469469

470+
def test_x_podman_in_pod_not_exists_command_line_in_pod_not_exists_env_var(self) -> None:
471+
"""
472+
Test that podman-compose will not create a pod when env var is set.
473+
"""
474+
command_up = [
475+
"python3",
476+
os.path.join(base_path(), "podman_compose.py"),
477+
"-f",
478+
os.path.join(
479+
base_path(),
480+
"tests",
481+
"integration",
482+
"in_pod",
483+
"custom_x-podman_not_exists",
484+
"docker-compose.yml",
485+
),
486+
"up",
487+
"-d",
488+
]
489+
490+
down_cmd = [
491+
"python3",
492+
podman_compose_path(),
493+
"-f",
494+
os.path.join(
495+
base_path(),
496+
"tests",
497+
"integration",
498+
"in_pod",
499+
"custom_x-podman_not_exists",
500+
"docker-compose.yml",
501+
),
502+
"down",
503+
]
504+
505+
env = {
506+
"PODMAN_COMPOSE_IN_POD": "0",
507+
}
508+
509+
try:
510+
self.run_subprocess_assert_returncode(
511+
command_up, failure_exitcode_when_rootful(), env=env
512+
)
513+
514+
finally:
515+
self.run_subprocess_assert_returncode(down_cmd, env=env)
516+
517+
command_rm_pod = ["podman", "pod", "rm", "pod_custom_x-podman_not_exists"]
518+
# can not actually find this pod because it was not created
519+
self.run_subprocess_assert_returncode(command_rm_pod, 1)
520+
470521
def test_x_podman_in_pod_custom_name(self) -> None:
471522
"""
472523
Test that podman-compose will create a pod with a custom name

tests/integration/test_utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ class RunSubprocessMixin:
3434
def is_debug_enabled(self) -> bool:
3535
return "TESTS_DEBUG" in os.environ
3636

37-
def run_subprocess(self, args: list[str]) -> tuple[bytes, bytes, int]:
37+
def run_subprocess(self, args: list[str], env: dict[str, str] = {}) -> tuple[bytes, bytes, int]:
3838
begin = time.time()
3939
if self.is_debug_enabled():
4040
print("TEST_CALL", args)
4141
proc = subprocess.Popen(
4242
args,
4343
stdout=subprocess.PIPE,
4444
stderr=subprocess.PIPE,
45+
env=os.environ | env,
4546
)
4647
out, err = proc.communicate()
4748
if self.is_debug_enabled():
@@ -51,9 +52,9 @@ def run_subprocess(self, args: list[str]) -> tuple[bytes, bytes, int]:
5152
return out, err, proc.returncode
5253

5354
def run_subprocess_assert_returncode(
54-
self, args: list[str], expected_returncode: int = 0
55+
self, args: list[str], expected_returncode: int = 0, env: dict[str, str] = {}
5556
) -> tuple[bytes, bytes]:
56-
out, err, returncode = self.run_subprocess(args)
57+
out, err, returncode = self.run_subprocess(args, env=env)
5758
decoded_out = out.decode('utf-8')
5859
decoded_err = err.decode('utf-8')
5960
self.assertEqual( # type: ignore[attr-defined]

tests/unit/test_container_to_args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ async def test_network_default_name(
650650
self, name: str, is_compat: bool, project_name: str, expected_network_name: str
651651
) -> None:
652652
c = create_compose_mock(project_name)
653-
c.x_podman = {"default_net_name_compat": is_compat}
653+
c.x_podman = {PodmanCompose.XPodmanSettingKey.DEFAULT_NET_NAME_COMPAT: is_compat}
654654
c.networks = {'network1': {}}
655655

656656
cnt = get_minimal_container()

0 commit comments

Comments
 (0)