Files
computer-playbook/plugins/lookup/compose_only.py
T
kevinveenbirkenbachandClaude Opus 4.7 587a3a1796 feat(swarm): consolidate container_name + pull_policy into generic compose_only lookup
Replace per-key `compose_container_name` lookup with a generic
`compose_only(key, value)` SPOT that emits `key: "<value>"` only in
compose mode and nothing in swarm. Same gating logic now serves
container_name (157 call sites) and pull_policy (17 call sites)
without duplicate plugin code.

Why this consolidation:
- The earlier `build-key` lint rule was misdirected. Swarm tolerates
  `build:` with a harmless warning when `image:` is pre-pushed (via
  the existing handlers/swarm_registry_push.py flow in
  roles/sys-svc-compose). The actual swarm-rejected key is
  `pull_policy:` ("Additional property pull_policy is not allowed"),
  and 10 templates were silently broken because they declared inline
  `pull_policy: never` outside the existing `{% if DEPLOYMENT_MODE
  \!= 'swarm' %}` gate in roles/sys-svc-container/templates/build.yml.j2.
- A single generic lookup is cleaner than N per-key wrappers when
  the gating logic is identical (emit-key-or-nothing).

Lint changes in tests/lint/ansible/jinja/test_mode_incompatible_keys.py:
- New `pull-policy-key` rule, regex `^\s*pull_policy\s*:`, remediation
  points at compose_only.
- Existing `container-name` rule keeps its raw-key regex; remediation
  now also points at compose_only.
- Broken-split / broken-embed regexes rekeyed on the `compose_only`
  lookup name (was `compose_container_name`).
- `build-key` rule and its 12 nocheck annotations across templates
  removed.

Coordinated changes:
- 86 templates rewritten (157 container_name calls migrated;
  17 inline pull_policy lines replaced).
- 1 redundant `{% if DEPLOYMENT_MODE \!= 'swarm' %}` guard dropped in
  build.yml.j2 (the lookup self-gates).
- 12 unit tests pin the compose_only contract.
- Catalog (suppression.md) updated: `pull-policy-key` row added,
  `container-name` row remediation updated.
- Old `plugins/lookup/compose_container_name.py` and its test module
  deleted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 02:24:50 +02:00

58 lines
2.0 KiB
Python

"""Lookup `compose_only`: emit a `key: "<value>"` YAML pair only in
compose mode; emit nothing under swarm.
A single SPOT for every service-level YAML key that is structurally
incompatible with `docker stack deploy` and must therefore be omitted
when DEPLOYMENT_MODE == 'swarm'. Currently used for:
* `container_name` — swarm rejects it alongside `deploy.replicas > 1`
("can't set container_name and X as container name must be unique").
* `pull_policy` — swarm rejects it as "Additional property pull_policy
is not allowed" (compose-only extension).
Both call sites look identical:
{{ lookup('compose_only', 'container_name', MY_CONTAINER) }}
{{ lookup('compose_only', 'pull_policy', 'never') }}
The value is always double-quoted in the output. That makes the emit
safe for YAML edge cases like `restart: no` (bare `no` would otherwise
parse as a boolean) without burdening the caller with quoting rules.
"""
from __future__ import annotations
import contextlib
from typing import Any
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
def run(
self,
terms: list[Any] | None,
variables: dict[str, Any] | None = None,
**kwargs: Any,
) -> list[str]:
if not terms or len(terms) != 2:
raise AnsibleError(
"compose_only lookup requires exactly two terms: the key and the value"
)
key = str(terms[0]).strip()
value = str(terms[1])
if not key:
raise AnsibleError("compose_only lookup: key term must be non-empty")
vars_ = variables or getattr(self._templar, "available_variables", {}) or {}
raw_mode = vars_.get("DEPLOYMENT_MODE", "compose")
templar = getattr(self, "_templar", None)
if templar is not None:
with contextlib.suppress(Exception):
raw_mode = templar.template(raw_mode)
if str(raw_mode).strip() == "swarm":
return [""]
return [f'{key}: "{value}"']