mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
Workers failed 'docker build: no Dockerfile': the build context, compose file, .env and runner binary were staged on the stack host only while every node deploys its own node-local runner stack. Build and push the image once on the manager (RUNNER_IMAGE_REF via the swarm registry, 04_image.yml) and let workers pull it; render compose.yml (template with lstrip_blocks, matching the replaced stack_host_template action), .env and the extracted run.sh on every node. Add a node_local kwarg to the compose_networks/container_networks/compose_restart/compose_only lookups so this node-local compose stack renders a bridge default network (workers cannot create overlays), keeps restart: unless-stopped (ephemeral runners died after one job) and keeps container_name under swarm; default is unchanged for every other role. Treat build/install.stamp as stale when the venv is missing and exclude ./build from the nested-src copy, closing the nested-e2e ruamel failure class. Regression tests for the node_local render paths; the network include tests now accept lookup kwargs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
2.3 KiB
Python
64 lines
2.3 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)
|
|
|
|
mode_force = vars_.get("compose_mode_force", "")
|
|
if templar is not None:
|
|
with contextlib.suppress(Exception):
|
|
mode_force = templar.template(mode_force)
|
|
effective_mode = str(mode_force or raw_mode).strip()
|
|
|
|
if effective_mode == "swarm" and not bool(kwargs.get("node_local", False)):
|
|
return [""]
|
|
return [f'{key}: "{value}"']
|