mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 12:17:13 +00:00
A role-level `placement: manager` pins every service of the role and keeps every volume node-local, so a role needing one node-bound service had to give up NFS for all of them. GitLab's gitaly is that case: its repositories volume is node-local (`nfs: false`, gitaly does not support NFS) and a reschedule left the data behind on the old node. `deploy.yml.j2` now resolves `services.<service>.placement` first and keeps the role-wide list as the fallback, so gitaly carries the constraint alone while webservice, sidekiq, workhorse, shell, migrations and gitlabmcp stay schedulable and `gitlab_shared`, `gitlab_uploads` and `gitlab_builds` keep their NFS backing. Role placement still comes from the primary entry, which is what drives that backing. Two things had to come with it. The swarm drill decided that a role is manager-pinned by grepping the whole `meta/services.yml`, so a per-service pin would have skipped GitLab's drain and reschedule steps while six of its services still run on workers; it now reads the primary entry. And `resource_filter` returns a present-but-null value rather than the hard default, which rendered `node.role == None`, a constraint no node satisfies. `test_no_placement_manager.py` now inspects every `placement` value instead of only `manager`, requires the justification for each, and rejects a value that is not a swarm node role, since a misspelled one hangs the converge wait rather than failing the deploy. Requirement 023 records the shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>