@@ -31,10 +31,12 @@ warns (ignored, behavior-neutral inside a single pod) or raises
3131- ** Service-key registry:** the declarative, uniformly-shaped flag keys —
3232 ` user ` , ` working_dir ` , ` platform ` , ` init ` , ` read_only ` , ` privileged ` ,
3333 ` group_add ` , ` cap_add ` , ` cap_drop ` , ` security_opt ` , ` devices ` , ` labels ` ,
34- ` annotations ` , ` extra_hosts ` , ` pull_policy ` , ` ulimits ` — are defined once,
35- each as a ` (validate, emit) ` pair, in the ** service-key registry**
36- (` SERVICE_KEYS ` in ` compose2pod/keys.py ` ); see ` architecture/glossary.md `
37- for the service-key spec / service-key registry / structural key terms.
34+ ` annotations ` , ` pull_policy ` , ` ulimits ` — are defined once, each as a
35+ ` (validate, emit) ` pair, in the ** service-key registry** (` SERVICE_KEYS ` in
36+ ` compose2pod/keys.py ` ); see ` architecture/glossary.md ` for the service-key
37+ spec / service-key registry / structural key terms. ` extra_hosts ` is a
38+ supported key but not a registry entry — it is pod-level, see Pod-level
39+ options below.
3840 The remaining keys documented below are ** structural keys** , handled
3941 outside the registry because their ` emit ` needs ` project_dir ` , spans
4042 multiple keys, or occupies the image/command slot.
@@ -67,9 +69,11 @@ warns (ignored, behavior-neutral inside a single pod) or raises
6769- ** ` annotations ` :** list or mapping, emitted as repeated ` --annotation `
6870 (` KEY=value ` , or bare ` KEY ` for a null value), sharing the ` _MAP_FLAGS `
6971 machinery with ` labels ` .
70- - ** ` extra_hosts ` :** list (` - host:ip ` ) or mapping (` host: ip ` ), emitted as
71- per-service ` --add-host host:ip ` . Distinct from the alias/hostname entries
72- (which resolve to ` 127.0.0.1 ` ); IPv6 values keep their colons.
72+ - ** ` extra_hosts ` :** list (` - host:ip ` ) or mapping (` host: ip ` ), pod-level like
73+ ` dns ` /` sysctls ` — see the Pod-level options section below. Distinct from
74+ the alias/hostname set in one respect: alias/hostname resolution is
75+ always fixed at ` 127.0.0.1 ` , while ` extra_hosts ` carries a user-specified
76+ address; IPv6 values keep their colons.
7377- ** ` pull_policy ` :** a validated enum mapped to podman's ` --pull `
7478 (` if_not_present ` → ` missing ` ; ` always ` /` never ` /` missing ` pass through),
7579 emitted literally. ` build ` and unknown values are rejected — compose2pod
@@ -181,36 +185,48 @@ it, so a handful of Compose keys cannot be per-container `podman run` flags.
181185compose2pod hoists them onto ` podman pod create ` instead
182186(` compose2pod/pod.py ` ) — the tool's only pod-create flags.
183187
184- - ** Supported:** ` dns ` , ` dns_search ` , ` dns_opt ` , ` sysctls ` — mapped to
185- ` --dns ` , ` --dns-search ` , ` --dns-option ` , ` --sysctl ` respectively (` _DNS_KEYS ` ,
188+ - ** Supported:** ` dns ` , ` dns_search ` , ` dns_opt ` , ` sysctls ` , ` extra_hosts ` —
189+ mapped to ` --dns ` , ` --dns-search ` , ` --dns-option ` , ` --sysctl ` , and (merged
190+ with the alias/hostname set) ` --add-host ` respectively (` _DNS_KEYS ` ,
186191 ` pod.py ` ).
187- - ** Aggregation is closure-scoped:** ` pod_create_flags(services, order) ` is
188- called with ` order ` — the target's dependency closure (` startup_order ` ) —
189- exactly like other closure-scoped constructs (secrets, configs). ` dns ` /
190- ` dns_search ` / ` dns_opt ` are unioned across the closure (deduplicated,
191- first-seen order); ` sysctls ` are unioned by key, and two services in the
192- closure setting the same key to different values is refused
193- (` UnsupportedComposeError: conflicting sysctl ... ` ) rather than resolved by
194- last-writer-wins.
192+ - ** Aggregation is closure-scoped:** `pod_create_flags(services, order,
193+ hosts)` is called with ` order` — the target's dependency closure
194+ (` startup_order ` ) — exactly like other closure-scoped constructs (secrets,
195+ configs). ` dns ` / ` dns_search ` / ` dns_opt ` are unioned across the closure
196+ (deduplicated, first-seen order); ` sysctls ` are unioned by key, and two
197+ services in the closure setting the same key to different values is
198+ refused (` UnsupportedComposeError: conflicting sysctl ... ` ) rather than
199+ resolved by last-writer-wins. ` --add-host ` is seeded from the
200+ alias/hostname set (` hosts ` , computed document-wide by ` graph.hostnames ` ,
201+ not closure-scoped — pre-existing, orthogonal behavior), then layered with
202+ each closure service's ` extra_hosts ` (closure-scoped like ` dns ` /` sysctls ` );
203+ a host name landing on two different addresses — across two services'
204+ ` extra_hosts ` , or between an ` extra_hosts ` entry and an alias's fixed
205+ ` 127.0.0.1 ` — is refused (` UnsupportedComposeError: conflicting host ... ` ),
206+ the same refuse-rather-than-guess rule as the ` sysctls ` conflict.
195207- ** Value shapes:** ` dns ` / ` dns_search ` / ` dns_opt ` accept a string or a list
196208 of strings; ` sysctls ` accepts a mapping (` key: value ` ) or a list of
197209 ` "key=value" ` strings, each value a string or number. A ` ${VAR} ` inside a
198210 value is wrapped in ` _Expand ` like other interpolated fields, so it stays
199211 live at run time and counts toward ` referenced_variables ` — the generated
200212 script's own shell expands it when it runs, not compose2pod at generation
201- time.
213+ time. ` --add-host ` entries render differently by source: an alias/hostname
214+ entry stays a plain unquoted token (pre-existing behavior, unchanged by
215+ this move), while an ` extra_hosts ` entry goes through ` _Expand ` (quoted,
216+ ` ${VAR} ` -live) — same as before it was per-service.
202217- ** Pod-wide divergence:** unlike every other service key, these apply to
203218 every container in the pod once emitted — including services that never
204- declared them — because the pod shares one ` /etc/resolv.conf ` and one
205- sysctl set. ` validate() ` (` compose2pod/parsing.py ` ) is target-agnostic
206- shape validation over the whole document: whenever any service anywhere
207- declares ` dns ` / ` dns_search ` / ` dns_opt ` / ` sysctls ` (` uses_pod_options ` ),
208- it emits the warning "dns/sysctls apply pod-wide -- all containers in the
209- pod share one /etc/resolv.conf and sysctl set", regardless of whether that
219+ declared them — because the pod shares one ` /etc/resolv.conf ` , one sysctl
220+ set, and one ` /etc/hosts ` . ` validate() ` (` compose2pod/parsing.py ` ) is
221+ target-agnostic shape validation over the whole document: whenever any
222+ service anywhere declares ` dns ` / ` dns_search ` / ` dns_opt ` / ` sysctls ` /
223+ ` extra_hosts ` (` uses_pod_options ` ), it emits the warning "dns/sysctls/
224+ extra_hosts apply pod-wide -- all containers in the pod share one
225+ /etc/resolv.conf, sysctl set, and /etc/hosts", regardless of whether that
210226 service turns out to be inside the target's closure. Conversely, at emit
211- time a ` dns ` / ` sysctls ` declaration on a service outside the target's
212- closure is silently ignored by ` pod_create_flags ` — no flag is emitted for
213- it, since that service is never run.
227+ time a ` dns ` / ` sysctls ` / ` extra_hosts ` declaration on a service outside
228+ the target's closure is silently ignored by ` pod_create_flags ` — no flag
229+ is emitted for it, since that service is never run.
214230- ** Non-goals:** per-service DNS/sysctls — impossible inside a
215231 shared-namespace pod, not a compose2pod limitation; last-writer-wins on a
216232 sysctl key conflict — refused instead, matching the refuse-on-conflict
@@ -444,13 +460,13 @@ enumeration ever appears to drift:
444460 the healthcheck ` test ` command.
445461- ** Service-key registry fields** whose spec wraps its value in ` _Expand ` —
446462 e.g. ` user ` , ` working_dir ` , ` platform ` , ` group_add ` , ` cap_add ` , ` cap_drop ` ,
447- ` security_opt ` , ` devices ` , ` labels ` , ` annotations ` , ` extra_hosts ` ,
448- ` ulimits ` , and every numeric resource-limit key (` mem_limit ` , ` cpus ` ,
449- ` pids_limit ` , ...). The rule, not the list, is authoritative: this is every
450- ` SERVICE_KEYS ` entry whose ` emit ` wraps its value (the
451- ` _scalar ` / ` _number_scalar ` / ` _list ` / ` _map ` factories, plus the custom
452- ` extra_hosts ` / ` ulimits ` emitters) except ` pull_policy ` (a validated enum
453- emitted verbatim from ` PULL_POLICY_MAP ` ) and the four boolean flags
463+ ` security_opt ` , ` devices ` , ` labels ` , ` annotations ` , ` ulimits ` , and every
464+ numeric resource-limit key (` mem_limit ` , ` cpus ` , ` pids_limit ` , ...). The
465+ rule, not the list, is authoritative: this is every ` SERVICE_KEYS ` entry
466+ whose ` emit ` wraps its value (the ` _scalar ` / ` _number_scalar ` / ` _list ` / ` _map `
467+ factories, plus the custom ` ulimits ` emitter) except ` pull_policy ` (a
468+ validated enum emitted verbatim from ` PULL_POLICY_MAP ` ) and the four
469+ boolean flags
454470 ` init ` /` read_only ` /` privileged ` /` oom_kill_disable ` (each emits a bare flag
455471 with no value to interpolate).
456472
0 commit comments