mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
A whitelist-scoped update preloaded and fully executed every provider role the host is a member of, because the gate only asked whether the role was in group_names. On this inventory that meant a single-app update built compose contexts, rendered configs and vhosts for 17 unrelated applications. reachable_roles() is lifted out of applications_if_group_and_all_deps() so the same traversal can start from an arbitrary seed set instead of group_names. The old function now calls it and filters as before, so every existing caller keeps its result. A new application_closure lookup exposes it to Ansible, and sys-service-loader gates each preload on the whitelist's closure. An empty whitelist yields an empty closure, which the gate reads as the full inventory scope, so a no-apps deploy is unchanged. Measured on mode=update apps=web-app-snipe-it: 42:49 before, 17:18 after, with ok tasks dropping from 4903 to 2030. Nothing outside the closure ran, and snipe-it's real dependencies (mariadb, redis, openldap, postgres, keycloak, mailu, prometheus, seaweedfs, cdn, logout) all still did. The closure also gates the required branch, deliberately. `required` reads as a global property but plugins/lookup/service.py derives it from group_names, so it means "some provisioned app needs this" and admits nearly every service. Exempting it was measured at 42:39 against 17:18 for the same deploy, which is the entire saving. A service whose provider lies outside the closure is by definition required by an application outside the deploy scope. Out of scope: seeding applications_current_play from the closure. That shrinks CURRENT_PLAY_DOMAINS_ALL, which cert SANs, openresty vhosts, DNS records and the webserver health check all consume at full inventory scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
64 lines
2.2 KiB
YAML
64 lines
2.2 KiB
YAML
---
|
|
# nocheck: run-once — main.yml is the orchestrator entry point and intentionally
|
|
# re-runnable; per-service idempotency is enforced by each loaded role's own
|
|
# run_once_<role> flag inside load_service.yml.
|
|
#
|
|
# SPOT: global loader for all shared services.
|
|
#
|
|
# Called from tasks/stages/01_constructor.yml before the normal server app pass.
|
|
#
|
|
# Two-phase contract:
|
|
# 1. Preload pass — iterate `_ordered_services` in dependency order and
|
|
# load each service body once (`service_loader_preloading: true`).
|
|
# 2. Post-load pass — flush the `service_loader_post_run` queue that
|
|
# roles registered during preload via this role's `list_or_shoot.yml`.
|
|
# Tasks listed there run with `service_loader_preloading: false`,
|
|
# so they see the fully preloaded service graph.
|
|
|
|
- name: "🔍 Discover ordered shared services"
|
|
ansible.builtin.set_fact:
|
|
_ordered_services: >-
|
|
{{
|
|
query(
|
|
'service_registry',
|
|
'ordered',
|
|
roles_dir=(lookup('path_absolute', 'roles'))
|
|
) | first
|
|
}}
|
|
|
|
- name: "🔍 Precompute required shared services (single applications load)"
|
|
ansible.builtin.set_fact:
|
|
_required_service_ids: >-
|
|
{{
|
|
query(
|
|
'service',
|
|
(_ordered_services | map(attribute='id') | list),
|
|
roles_dir=(lookup('path_absolute', 'roles'))
|
|
)
|
|
| selectattr('required')
|
|
| map(attribute='id')
|
|
| list
|
|
}}
|
|
|
|
- name: "🔍 Scope the preload to the whitelist's dependency closure"
|
|
ansible.builtin.set_fact:
|
|
_preload_closure: "{{ lookup('application_closure', lookup('deployment').whitelist) }}"
|
|
|
|
- name: "🔌 Load shared service"
|
|
include_tasks: load_service.yml
|
|
vars:
|
|
service_key: "{{ service_item.id }}"
|
|
service_role: "{{ service_item.role }}"
|
|
service_run_once_var: "{{ 'run_once_' ~ (service_item.role | replace('-', '_')) }}"
|
|
loop: "{{ _ordered_services }}"
|
|
loop_control:
|
|
loop_var: service_item
|
|
label: "{{ service_item.id }}"
|
|
|
|
- name: "▶️ Run service-loader post-load roles"
|
|
ansible.builtin.include_role:
|
|
name: "{{ post_run_role }}"
|
|
loop: "{{ service_loader_post_run | unique }}"
|
|
loop_control:
|
|
loop_var: post_run_role
|