Files
computer-playbook/cli/administration/deploy/swarm/closure.py
kevinveenbirkenbachandClaude Opus 4.8 09a131d007 refactor(placement): rename meta key default_placement to placement
Rename the meta/services.yml key across all roles, the roles_with_default_placement lookup (now roles_with_placement), the get_role_default_placement / iter_roles_with_default_placement helpers, and every consumer (swarm closure, compose_volumes filter, nfs_prep, container deploy template, constructor stage, extend_inventory) plus their tests and docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:19:08 +02:00

108 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Closure for the swarm deploy CLI's ``--id`` list.
Uses the same ``applications_if_group_and_all_deps`` dep-walker the
runtime constructor consumes, so the CLI-side closure and the
constructor's in-play role set stay in sync without duplicated
meta-walking logic.
"""
from __future__ import annotations
from pathlib import Path
from utils import PROJECT_ROOT
from utils.cache.applications import get_merged_applications
from utils.inventory.groups import inventory_has_group
from utils.roles.applications.in_group_deps import (
applications_if_group_and_all_deps,
)
from utils.roles.meta_lookup import iter_roles_with_placement
_SWARM_MANAGER_GROUP = "svc-swarm-manager"
_MANAGER_PLACEMENT = "manager"
def is_swarm_inventory(inventory_path: str) -> bool:
return inventory_has_group(inventory_path, _SWARM_MANAGER_GROUP)
def swarm_infra_closure(inventory_path: str) -> list[str]:
"""Inventory-present roles with ``placement: manager``.
Safety net for shared infra (``svc-registry-cache``, ...) that no
application's ``meta/services.yml`` explicitly depends on.
"""
if not is_swarm_inventory(inventory_path):
return []
return [
role
for role in iter_roles_with_placement(_MANAGER_PLACEMENT)
if inventory_has_group(inventory_path, role)
]
def inventory_role_groups(
inventory_path: str, roles_dir: str | Path | None = None
) -> list[str]:
"""Inventory groups whose name matches a directory under ``roles/``."""
base = Path(roles_dir) if roles_dir is not None else PROJECT_ROOT / "roles"
if not base.is_dir():
return []
available = {p.name for p in base.iterdir() if p.is_dir()}
found = {name for name in available if inventory_has_group(inventory_path, name)}
return sorted(found)
def _dep_walk_closure(
seed: list[str], roles_dir: str | Path | None = None
) -> list[str]:
if not seed:
return []
base = Path(roles_dir) if roles_dir is not None else PROJECT_ROOT / "roles"
applications = get_merged_applications(roles_dir=str(base))
if not applications:
return list(seed)
expanded = applications_if_group_and_all_deps(
applications,
list(seed),
project_root=str(base.parent),
roles_dir=str(base),
)
return list(expanded.keys())
def swarm_deploy_targets(
operator_ids: list[str] | None,
inventory_path: str,
roles_dir: str | Path | None = None,
) -> list[str]:
"""Effective ``--id`` list: seed ∪ dep-walk(seed) ∪ default-placement safety net.
Seed is ``operator_ids`` if given, else every inventory role group.
Operator/inventory entries keep their original order; dep-walk
additions are appended in alpha order so reruns produce stable
diffs.
"""
seed: list[str] = list(operator_ids or [])
if not seed:
seed = list(inventory_role_groups(inventory_path, roles_dir=roles_dir))
walked = set(_dep_walk_closure(seed, roles_dir=roles_dir))
safety_net = swarm_infra_closure(inventory_path)
inventory_groups = set(inventory_role_groups(inventory_path, roles_dir=roles_dir))
base = list(seed)
seen = set(base)
for role in sorted(walked):
if role in seen:
continue
if role not in inventory_groups:
continue
base.append(role)
seen.add(role)
for role in safety_net:
if role not in seen:
base.append(role)
seen.add(role)
return base