compose2pod 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {compose2pod-0.4.0 → compose2pod-0.4.2}/PKG-INFO +1 -1
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/emit.py +16 -2
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/keys.py +9 -2
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/parsing.py +14 -14
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/resources.py +8 -8
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/stores.py +4 -4
- {compose2pod-0.4.0 → compose2pod-0.4.2}/pyproject.toml +1 -1
- {compose2pod-0.4.0 → compose2pod-0.4.2}/README.md +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/__init__.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/__main__.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/cli.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/exceptions.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/extends.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/graph.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/healthcheck.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/pod.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/py.typed +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/read.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/shell.py +0 -0
- {compose2pod-0.4.0 → compose2pod-0.4.2}/compose2pod/values.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compose2pod
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Convert a Docker Compose file into a script that runs its services as a single Podman pod
|
|
5
5
|
Keywords: podman,docker-compose,compose,pod,ci,testing,containers
|
|
6
6
|
Author: Artur Shiriev
|
|
@@ -335,7 +335,13 @@ def _plan(compose: dict[str, Any], options: EmitOptions) -> PlannedScript:
|
|
|
335
335
|
# Only the closure joins the pod, so only the closure is resolvable. A name
|
|
336
336
|
# pointing at 127.0.0.1 for a service that never runs would turn an honest
|
|
337
337
|
# resolution failure into a connection-refused.
|
|
338
|
-
|
|
338
|
+
# The pod name is added as a resolvable name so every container's own
|
|
339
|
+
# hostname (set to the pod name below) resolves. A pod shares one UTS
|
|
340
|
+
# namespace, so without this the hostname is the random container ID,
|
|
341
|
+
# absent from the owned /etc/hosts -- `hostname -f` then fails and images
|
|
342
|
+
# that resolve their own hostname at startup crash. Podman used to add this
|
|
343
|
+
# line itself; `--no-hosts` stops it, so the script owns it too.
|
|
344
|
+
hosts = [*hostnames({name: services[name] for name in order}), options.pod]
|
|
339
345
|
host_tokens = hosts_file_tokens(services, order, hosts)
|
|
340
346
|
completion_gated = {
|
|
341
347
|
dep
|
|
@@ -355,12 +361,20 @@ def _plan(compose: dict[str, Any], options: EmitOptions) -> PlannedScript:
|
|
|
355
361
|
lines.append(f"trap '{teardown}' EXIT")
|
|
356
362
|
pod_flags = pod_create_flags(services, order)
|
|
357
363
|
_collect_vars(pod_flags, names)
|
|
358
|
-
|
|
364
|
+
# `--uts private` gives the pod its own UTS namespace so `--hostname` is
|
|
365
|
+
# settable (in the host UTS namespace Podman refuses it); the pod name is
|
|
366
|
+
# the shared hostname, resolvable via the line added to the hosts file above.
|
|
367
|
+
pod_name = shlex.quote(options.pod)
|
|
368
|
+
pod_create = f"podman pod create --name {pod_name} --no-hosts --uts private --hostname {pod_name}"
|
|
359
369
|
if pod_flags:
|
|
360
370
|
pod_create += " " + _render(pod_flags)
|
|
361
371
|
lines.append(pod_create)
|
|
362
372
|
_collect_vars(host_tokens, names)
|
|
363
373
|
lines.append(f"printf '%s\\n' {_render(host_tokens)} > \"$hostsfile\"")
|
|
374
|
+
# mktemp creates the file 0600; make it world-readable so a service image
|
|
375
|
+
# running as a non-root user can read the bind-mounted /etc/hosts. Without
|
|
376
|
+
# this, glibc falls through to DNS and name resolution fails.
|
|
377
|
+
lines.append('chmod 644 "$hostsfile"')
|
|
364
378
|
lines.extend(stores.create_lines(compose, order, options.pod, options.project_dir))
|
|
365
379
|
names |= stores.referenced_variables(compose, order, options.project_dir)
|
|
366
380
|
waited: set[str] = set()
|
|
@@ -111,8 +111,8 @@ def is_number(value: Any) -> bool: # noqa: ANN401 - Compose values are untyped
|
|
|
111
111
|
return not isinstance(value, bool) and isinstance(value, int | float | str)
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def require_string_keys(where: str, mapping: dict[Any, Any]) ->
|
|
115
|
-
"""Check every key of a raw YAML/JSON mapping is a string.
|
|
114
|
+
def require_string_keys(where: str, mapping: dict[Any, Any]) -> set[str]:
|
|
115
|
+
"""Check every key of a raw YAML/JSON mapping is a string; return them.
|
|
116
116
|
|
|
117
117
|
PyYAML routinely produces non-string keys (a bare `3:` is an int; under
|
|
118
118
|
YAML 1.1, a bare `on:`/`off:` is a bool). Every mapping-key consumer
|
|
@@ -120,11 +120,18 @@ def require_string_keys(where: str, mapping: dict[Any, Any]) -> None:
|
|
|
120
120
|
`str` and crashes raw otherwise. This is the one shared check the gate
|
|
121
121
|
runs before it reads any of `mapping`'s keys, so a non-string key fails
|
|
122
122
|
clean regardless of which mapping it turned up in.
|
|
123
|
+
|
|
124
|
+
Returns the validated keys as a `set[str]` so an unsupported-key check can
|
|
125
|
+
build on it directly (`require_string_keys(...) - KNOWN`) with a statically
|
|
126
|
+
sortable type, instead of re-deriving `set(mapping)` as `set[object]`.
|
|
123
127
|
"""
|
|
128
|
+
keys: set[str] = set()
|
|
124
129
|
for key in mapping:
|
|
125
130
|
if not isinstance(key, str):
|
|
126
131
|
msg = f"{where}: key {key!r} must be a string"
|
|
127
132
|
raise UnsupportedComposeError(msg)
|
|
133
|
+
keys.add(key)
|
|
134
|
+
return keys
|
|
128
135
|
|
|
129
136
|
|
|
130
137
|
def _validate_list_elements(name: str, key: str, value: list[Any]) -> None:
|
|
@@ -216,12 +216,12 @@ def _validate_volume_long_form(name: str, entry: dict[str, Any]) -> None:
|
|
|
216
216
|
here) because the check itself needs `vtype` to know which sub-map key --
|
|
217
217
|
`bind`/`volume`/`tmpfs` -- the entry is allowed to carry alongside it.
|
|
218
218
|
"""
|
|
219
|
-
require_string_keys(f"service {name!r}: volume", entry)
|
|
219
|
+
keys = require_string_keys(f"service {name!r}: volume", entry)
|
|
220
220
|
vtype = entry.get("type")
|
|
221
221
|
if vtype not in _VOLUME_LONG_TYPES:
|
|
222
222
|
msg = f"service {name!r}: volume 'type' must be one of {list(_VOLUME_LONG_TYPES)}"
|
|
223
223
|
raise UnsupportedComposeError(msg)
|
|
224
|
-
unknown =
|
|
224
|
+
unknown = keys - _VOLUME_LONG_KEYS - {vtype}
|
|
225
225
|
if unknown:
|
|
226
226
|
msg = f"service {name!r}: volume: unsupported keys {sorted(unknown)}"
|
|
227
227
|
raise UnsupportedComposeError(msg)
|
|
@@ -333,8 +333,8 @@ def _validate_volume_options(name: str, vtype: str, options: Any) -> None: # no
|
|
|
333
333
|
if not isinstance(options, dict):
|
|
334
334
|
msg = f"service {name!r}: {vtype} options must be a mapping"
|
|
335
335
|
raise UnsupportedComposeError(msg)
|
|
336
|
-
require_string_keys(f"service {name!r}: {vtype} options", options)
|
|
337
|
-
unknown =
|
|
336
|
+
keys = require_string_keys(f"service {name!r}: {vtype} options", options)
|
|
337
|
+
unknown = keys - _VOLUME_OPTION_KEYS[vtype]
|
|
338
338
|
if unknown:
|
|
339
339
|
msg = f"service {name!r}: {vtype} options: unsupported keys {sorted(unknown)}"
|
|
340
340
|
raise UnsupportedComposeError(msg)
|
|
@@ -529,8 +529,8 @@ def _validate_build_secret_entry(name: str, entry: Any) -> None: # noqa: ANN401
|
|
|
529
529
|
if not isinstance(entry, dict):
|
|
530
530
|
msg = f"service {name!r}: build 'secrets' entries must be a string or mapping"
|
|
531
531
|
raise UnsupportedComposeError(msg)
|
|
532
|
-
require_string_keys(f"service {name!r}: build 'secrets' entry", entry)
|
|
533
|
-
unknown =
|
|
532
|
+
keys = require_string_keys(f"service {name!r}: build 'secrets' entry", entry)
|
|
533
|
+
unknown = keys - _BUILD_SECRET_REF_KEYS
|
|
534
534
|
if unknown:
|
|
535
535
|
msg = f"service {name!r}: build 'secrets' entry: unsupported keys {sorted(unknown)}"
|
|
536
536
|
raise UnsupportedComposeError(msg)
|
|
@@ -683,8 +683,8 @@ def _validate_env_file(name: str, svc: dict[str, Any]) -> None:
|
|
|
683
683
|
|
|
684
684
|
def _validate_env_file_mapping(name: str, entry: dict[str, Any]) -> None:
|
|
685
685
|
"""Check one long-form env_file mapping against Docker's strict schema (measured, v5.1.2)."""
|
|
686
|
-
require_string_keys(f"service {name!r}: 'env_file'", entry)
|
|
687
|
-
unknown =
|
|
686
|
+
keys = require_string_keys(f"service {name!r}: 'env_file'", entry)
|
|
687
|
+
unknown = keys - _ENV_FILE_ENTRY_KEYS
|
|
688
688
|
if unknown:
|
|
689
689
|
msg = f"service {name!r}: 'env_file' entry: unsupported keys {sorted(unknown)}"
|
|
690
690
|
raise UnsupportedComposeError(msg)
|
|
@@ -1240,8 +1240,8 @@ def _validate_definition_external(label: str) -> Callable[[str, str, Any], None]
|
|
|
1240
1240
|
if values.is_bool_like(value):
|
|
1241
1241
|
return
|
|
1242
1242
|
if isinstance(value, dict):
|
|
1243
|
-
require_string_keys(f"{label} {ident!r}: {key!r}", value)
|
|
1244
|
-
unknown =
|
|
1243
|
+
keys = require_string_keys(f"{label} {ident!r}: {key!r}", value)
|
|
1244
|
+
unknown = keys - _EXTERNAL_MAP_KEYS
|
|
1245
1245
|
if unknown:
|
|
1246
1246
|
msg = f"{label} {ident!r}: {key!r}: unsupported keys {sorted(unknown)}"
|
|
1247
1247
|
raise UnsupportedComposeError(msg)
|
|
@@ -1278,8 +1278,8 @@ def _validate_ipam_config_entry(label: str, ident: str, entry: Any) -> None: #
|
|
|
1278
1278
|
if not isinstance(entry, dict):
|
|
1279
1279
|
msg = f"{label} {ident!r}: ipam 'config' entries must be a mapping"
|
|
1280
1280
|
raise UnsupportedComposeError(msg)
|
|
1281
|
-
require_string_keys(f"{label} {ident!r}: ipam config entry", entry)
|
|
1282
|
-
unknown =
|
|
1281
|
+
keys = require_string_keys(f"{label} {ident!r}: ipam config entry", entry)
|
|
1282
|
+
unknown = keys - _IPAM_CONFIG_ENTRY_KEYS
|
|
1283
1283
|
if unknown:
|
|
1284
1284
|
msg = f"{label} {ident!r}: ipam config entry: unsupported keys {sorted(unknown)}"
|
|
1285
1285
|
raise UnsupportedComposeError(msg)
|
|
@@ -1309,8 +1309,8 @@ def _validate_definition_ipam(label: str) -> Callable[[str, str, Any], None]:
|
|
|
1309
1309
|
if not isinstance(value, dict):
|
|
1310
1310
|
msg = f"{label} {ident!r}: {key!r} must be a mapping"
|
|
1311
1311
|
raise UnsupportedComposeError(msg)
|
|
1312
|
-
require_string_keys(f"{label} {ident!r}: {key!r}", value)
|
|
1313
|
-
unknown =
|
|
1312
|
+
keys = require_string_keys(f"{label} {ident!r}: {key!r}", value)
|
|
1313
|
+
unknown = keys - _IPAM_KEYS
|
|
1314
1314
|
if unknown:
|
|
1315
1315
|
msg = f"{label} {ident!r}: {key!r}: unsupported keys {sorted(unknown)}"
|
|
1316
1316
|
raise UnsupportedComposeError(msg)
|
|
@@ -36,8 +36,8 @@ def _validate_limits(name: str, svc: dict[str, Any], limits: Any) -> None: # no
|
|
|
36
36
|
if not isinstance(limits, dict):
|
|
37
37
|
msg = f"service {name!r}: deploy.resources.limits must be a mapping"
|
|
38
38
|
raise UnsupportedComposeError(msg)
|
|
39
|
-
require_string_keys(f"service {name!r}: deploy.resources.limits", limits)
|
|
40
|
-
unknown =
|
|
39
|
+
keys = require_string_keys(f"service {name!r}: deploy.resources.limits", limits)
|
|
40
|
+
unknown = keys - set(_LIMITS)
|
|
41
41
|
if unknown:
|
|
42
42
|
msg = f"service {name!r}: deploy.resources.limits: unsupported keys {sorted(unknown)}"
|
|
43
43
|
raise UnsupportedComposeError(msg)
|
|
@@ -54,8 +54,8 @@ def _validate_reservations(name: str, svc: dict[str, Any], reservations: Any) ->
|
|
|
54
54
|
if not isinstance(reservations, dict):
|
|
55
55
|
msg = f"service {name!r}: deploy.resources.reservations must be a mapping"
|
|
56
56
|
raise UnsupportedComposeError(msg)
|
|
57
|
-
require_string_keys(f"service {name!r}: deploy.resources.reservations", reservations)
|
|
58
|
-
unknown =
|
|
57
|
+
keys = require_string_keys(f"service {name!r}: deploy.resources.reservations", reservations)
|
|
58
|
+
unknown = keys - {"cpus", "memory", "devices"}
|
|
59
59
|
if unknown:
|
|
60
60
|
msg = f"service {name!r}: deploy.resources.reservations: unsupported keys {sorted(unknown)}"
|
|
61
61
|
raise UnsupportedComposeError(msg)
|
|
@@ -91,8 +91,8 @@ def validate_deploy(name: str, svc: dict[str, Any]) -> None:
|
|
|
91
91
|
if not isinstance(deploy, dict):
|
|
92
92
|
msg = f"service {name!r}: 'deploy' must be a mapping"
|
|
93
93
|
raise UnsupportedComposeError(msg)
|
|
94
|
-
require_string_keys(f"service {name!r}: deploy", deploy)
|
|
95
|
-
unknown =
|
|
94
|
+
keys = require_string_keys(f"service {name!r}: deploy", deploy)
|
|
95
|
+
unknown = keys - {"resources"}
|
|
96
96
|
if unknown:
|
|
97
97
|
msg = f"service {name!r}: deploy: only 'resources' is supported (got {sorted(unknown)})"
|
|
98
98
|
raise UnsupportedComposeError(msg)
|
|
@@ -105,8 +105,8 @@ def validate_deploy(name: str, svc: dict[str, Any]) -> None:
|
|
|
105
105
|
raise UnsupportedComposeError(msg)
|
|
106
106
|
_reject_null_block(name, "deploy.resources.limits", resources, "limits")
|
|
107
107
|
_reject_null_block(name, "deploy.resources.reservations", resources, "reservations")
|
|
108
|
-
require_string_keys(f"service {name!r}: deploy.resources", resources)
|
|
109
|
-
unknown =
|
|
108
|
+
keys = require_string_keys(f"service {name!r}: deploy.resources", resources)
|
|
109
|
+
unknown = keys - {"limits", "reservations"}
|
|
110
110
|
if unknown:
|
|
111
111
|
msg = f"service {name!r}: deploy.resources: unsupported keys {sorted(unknown)}"
|
|
112
112
|
raise UnsupportedComposeError(msg)
|
|
@@ -73,8 +73,8 @@ def _validate_def(name: str, definition: Any, kind: StoreKind) -> None: # noqa:
|
|
|
73
73
|
if not isinstance(definition, dict):
|
|
74
74
|
msg = f"{kind.label} {name!r} must be a mapping"
|
|
75
75
|
raise UnsupportedComposeError(msg)
|
|
76
|
-
require_string_keys(f"{kind.label} {name!r}", definition)
|
|
77
|
-
unknown =
|
|
76
|
+
keys = require_string_keys(f"{kind.label} {name!r}", definition)
|
|
77
|
+
unknown = keys - kind.sources
|
|
78
78
|
if unknown:
|
|
79
79
|
if "external" in unknown:
|
|
80
80
|
msg = (
|
|
@@ -131,8 +131,8 @@ def _ref_source(name: str, ref: Any, kind: StoreKind) -> str: # noqa: ANN401 -
|
|
|
131
131
|
if not isinstance(ref, dict):
|
|
132
132
|
msg = f"service {name!r}: {kind.label} entry must be a string or mapping"
|
|
133
133
|
raise UnsupportedComposeError(msg)
|
|
134
|
-
require_string_keys(f"service {name!r}: {kind.label} entry", ref)
|
|
135
|
-
unknown =
|
|
134
|
+
keys = require_string_keys(f"service {name!r}: {kind.label} entry", ref)
|
|
135
|
+
unknown = keys - _LONG_FORM_KEYS
|
|
136
136
|
if unknown:
|
|
137
137
|
msg = f"service {name!r}: unsupported {kind.label} keys {sorted(unknown)}"
|
|
138
138
|
raise UnsupportedComposeError(msg)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|