feat(ansible): parameterize docker_prune with prune_target (containers/images/system)
M78 step 1. The bundled maintenance/docker_prune.yml was a single
`docker system prune -f`; the upcoming per-host prune buttons need a
granular split. Add a `prune_target` extra-var:
- containers → docker container prune -f
- images → docker image prune -f (+ -a when prune_all_images)
- system → docker system prune -f (+ -a / --volumes) — the DEFAULT,
so existing manual/scheduled callers (which set no var)
keep today's behavior.
Reuses the existing bundled playbook rather than adding parallel files;
keeps the description/category/confirm meta so it still self-describes as
a confirm-gated maintenance run. Adds unit coverage: the playbook stays
valid YAML, its meta still discovers, and all three targets are guarded.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CAGR73DUowdVFVvYzLXC5C
This commit is contained in:
@@ -1,11 +1,21 @@
|
||||
"""The bundled first-party playbook source is always present and discoverable."""
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
from steward.ansible.sources import (
|
||||
BUILTIN_SOURCE_NAME,
|
||||
discover_playbook_meta,
|
||||
discover_playbooks,
|
||||
get_sources,
|
||||
)
|
||||
|
||||
|
||||
def _bundled_content(rel_path: str) -> str:
|
||||
builtin = get_sources({"sources": []})[0]
|
||||
return (Path(builtin["path"]) / rel_path).read_text()
|
||||
|
||||
|
||||
def test_builtin_source_is_first_and_local():
|
||||
sources = get_sources({"sources": []})
|
||||
assert sources[0]["name"] == BUILTIN_SOURCE_NAME
|
||||
@@ -17,3 +27,33 @@ def test_bundled_playbooks_are_discoverable():
|
||||
playbooks = discover_playbooks(builtin["path"])
|
||||
assert "maintenance/docker_prune.yml" in playbooks
|
||||
assert "host_agent/install.yml" in playbooks
|
||||
|
||||
|
||||
def test_docker_prune_playbook_parses_and_keeps_meta():
|
||||
"""The prune playbook stays valid YAML and keeps its self-describing meta
|
||||
(a destructive maintenance run that must prompt for confirmation)."""
|
||||
content = _bundled_content("maintenance/docker_prune.yml")
|
||||
plays = yaml.safe_load(content)
|
||||
assert isinstance(plays, list) and plays # at least one play
|
||||
|
||||
meta = discover_playbook_meta(content)
|
||||
assert meta["confirm"] is True
|
||||
assert meta["category"] == "maintenance"
|
||||
assert meta["description"]
|
||||
|
||||
|
||||
def test_docker_prune_supports_all_three_targets():
|
||||
"""M78 drives the three prune buttons via a single `prune_target` var;
|
||||
`system` is the default so pre-M78 callers (no var set) are unchanged."""
|
||||
content = _bundled_content("maintenance/docker_prune.yml")
|
||||
play = yaml.safe_load(content)[0]
|
||||
assert play["vars"]["prune_target"] == "system"
|
||||
|
||||
# Every target the routes/UI can send has a matching guarded task.
|
||||
guards = {
|
||||
task.get("when")
|
||||
for task in play["tasks"]
|
||||
if isinstance(task.get("when"), str) and "prune_target" in task["when"]
|
||||
}
|
||||
for target in ("containers", "images", "system"):
|
||||
assert f"prune_target == '{target}'" in guards
|
||||
|
||||
Reference in New Issue
Block a user