Files
computer-playbook/tests/unit/plugins/action/test_stack_host_template.py
T
kevinveenbirkenbachandClaude Opus 4.7 05f6a97bd8 refactor(stack_host_template): generalise IS_STACK_HOST template wrapper across the codebase
Renames the local action plugin from `conf_template` to
`stack_host_template` so the name carries the actual semantics
(IS_STACK_HOST gate, not nginx config), broadens the lint guard to
every bare `template:` task, and migrates every consumer found in the
audit.

Plugin / tests / lint:
  * plugins/action/conf_template.py -> plugins/action/stack_host_template.py
    (drop nginx-specific docstring; trust_as_template gate unchanged)
  * test_conf_template.py            -> test_stack_host_template.py
  * test_conf_template_migration.py  -> test_stack_host_template_migration.py
    Scope expanded from `.conf.j2` to every `template:` task; marker
    renamed to `nocheck: stack-host-template`.

Migrations (~50 tasks now using stack_host_template:):
  * sys-svc-compose/04_files.yml: compose.yml, .env, Dockerfile,
    compose.override (every per-app compose-tree file)
  * web-app-keycloak: sso_proxy upstream-config (fixes the friendica
    CI failure that prompted this refactor) + realm-import
  * web-app-taiga, web-app-matrix, web-app-opentalk, web-app-pgadmin,
    web-app-moodle, web-app-listmonk, web-app-littlejs, web-app-fediwall,
    web-app-wordpress, web-app-discourse, web-app-bigbluebutton,
    web-app-jenkins, web-app-jitsi, web-app-mobilizon, web-app-suitecrm,
    web-app-decidim, web-app-dashboard, web-app-nextcloud, web-app-xwiki,
    web-app-mailu, web-app-magento, web-app-odoo, web-app-prometheus,
    web-app-matrix, web-app-peertube, web-app-mailu (+ existing `conf_template:`
    callers updated to the new name)
  * sys-front-tls, svc-db-openldap, sys-svc-webserver-core,
    sys-svc-rdbms (dedicated DB env + initdb), sys-front-inj-css,
    sys-front-inj-dashboard, sys-front-inj-all, sys-stk-front-proxy,
    web-svc-cdn-css, web-svc-xmpp, web-svc-legal, web-svc-simpleicons,
    web-opt-rdr-domains

Nochecks (per-host or already-structurally-stack-host):
  * sys-svc-mail-smtp/postfix, sys-svc-sshd, sys-svc-container/daemon.json
  * sys-timer, sys-service, sys-svc-mail-msmtp, sys-svc-journalctl,
    sys-daemon, sys-svc-compose/utils/_render_one_template (delegate_to
    stack host with peer slurp dance)
  * dev-locales, dev-nix, desk-*, user, user-backup, user-workstation
  * svc-storage-nfs-server/exports, test-e2e-playwright stage dirs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 15:45:38 +02:00

111 lines
3.7 KiB
Python

import unittest
from unittest.mock import patch
from plugins.action.stack_host_template import ActionModule
class _FakeTask:
def __init__(self, *, args=None):
self.args = {} if args is None else dict(args)
class _FakeTemplar:
def __init__(self, *, is_stack_host=True):
self._is_stack_host = is_stack_host
self.available_variables: dict[str, object] = {}
def template(self, raw):
return self._is_stack_host
def _make_action(task, templar):
action = object.__new__(ActionModule)
action._task = task
action._templar = templar
return action
class TestStackHostTemplate(unittest.TestCase):
def test_skips_silently_on_non_stack_host(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf"})
action = _make_action(task, _FakeTemplar(is_stack_host=False))
result = action.run(task_vars={})
self.assertTrue(result["skipped"])
self.assertFalse(result["changed"])
self.assertIn("IS_STACK_HOST", result["skip_reason"])
def test_skips_when_templar_returns_string_false(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf"})
action = _make_action(task, _FakeTemplar(is_stack_host="False"))
result = action.run(task_vars={})
self.assertTrue(result["skipped"])
def test_delegates_to_template_on_stack_host(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf"})
action = _make_action(task, _FakeTemplar(is_stack_host=True))
with patch(
"plugins.action.stack_host_template.TemplateActionModule.run",
return_value={"changed": True},
) as super_run:
action.run(task_vars={})
super_run.assert_called_once()
def test_seeds_templar_with_task_vars_before_rendering(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf"})
templar = _FakeTemplar(is_stack_host=True)
action = _make_action(task, templar)
with patch(
"plugins.action.stack_host_template.TemplateActionModule.run",
return_value={"changed": True},
):
action.run(task_vars={"IS_STACK_HOST": True, "ANSIBLE_VERSION": "9.0"})
self.assertEqual(templar.available_variables.get("IS_STACK_HOST"), True)
self.assertEqual(templar.available_variables.get("ANSIBLE_VERSION"), "9.0")
def test_defaults_mode_to_0644_when_unset(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf"})
action = _make_action(task, _FakeTemplar(is_stack_host=True))
with patch(
"plugins.action.stack_host_template.TemplateActionModule.run",
return_value={"changed": True},
):
action.run(task_vars={})
self.assertEqual(task.args["mode"], "0644")
def test_keeps_caller_supplied_mode(self):
task = _FakeTask(args={"src": "x.j2", "dest": "/etc/x.conf", "mode": "0600"})
action = _make_action(task, _FakeTemplar(is_stack_host=True))
with patch(
"plugins.action.stack_host_template.TemplateActionModule.run",
return_value={"changed": True},
):
action.run(task_vars={})
self.assertEqual(task.args["mode"], "0600")
def test_is_stack_host_expr_carries_trust_tag(self):
from ansible.template import is_trusted_as_template
from plugins.action.stack_host_template import _IS_STACK_HOST_EXPR
self.assertTrue(
is_trusted_as_template(_IS_STACK_HOST_EXPR),
"The IS_STACK_HOST template expression must carry the TrustedAsTemplate "
"tag; otherwise Ansible 2.19+ returns the literal string unchanged.",
)
if __name__ == "__main__":
unittest.main()