Files
computer-playbook/roles/web-app-nextcloud/files
kevinveenbirkenbachandClaude Opus 5 f3b0d8e6a2 fix(init-gate): stop waiting for a version only a task past the gate produces
Both init gates made every replica other than slot 1 block until the installed
version equalled its own image's. Only a task that gets past the gate runs the
upgrade, so the gate waited on a state it was itself preventing. In CI run
38818071194 the floating tag nextcloud:34-fpm-alpine moved from 34.0.4 to
34.0.5 between the job's two deploys, swarm handed the new image to slot 2
first, and the service sat at 2/3 with UpdateStatus updating until the
converge wait exhausted 600 attempts. php-fpm never started, the healthcheck
failed, and swarm killed and recreated an identical task in a loop.

Delete the version comparison. The lock probe and the installed, maintenance
and needsDbUpgrade conditions all stay, so the gate still cannot release a
replica onto a tree under modification. The version decision belongs to the
upstream entrypoint, which makes it under a blocking flock on the same
*-init-sync.lock the gate probes, re-reads the installed version after
acquiring it, and exits non-zero on a downgrade.

That contract is measured, not assumed. The pulled image's /entrypoint.sh is
byte identical to upstream's, and its own version expression - the one the
deleted line compared against - prints 34.0.5.1 against the 34.0.4.1 the live
stack reported. A harness drives that entrypoint against a faked tree: equal
versions skip the upgrade and exec, an installed version higher than the image
refuses and never execs, and a peer holding the lock makes it block, re-read
and skip. The re-read also closes the gate's own TOCTOU window, because a peer
that takes the lock in the gap cannot be overtaken.

Old and new gate were run side by side over 17 states for both roles: 13
behave identically and the 4 that differ are all states where the old gate
waited forever. Deleting a condition can only change behaviour where it was
false, and where it was false the old outcome was never a working one. The
deleted line was also a substring match, so an image at 34.0.4.1 matched a
tree reporting 34.0.4.10.

Friendica carried the same shape and is fixed with it. Its pinned image maps to
an entrypoint that takes the lock blocking, reads both versions after
acquiring, refuses a downgrade and upgrades under the lock, verified the same
way.

The lint fails if an image-version equality returns to any init gate, and also
requires every gate to keep probing its lock, because that probe is what makes
the deletion safe. Putting the deleted line back makes it fail naming the file
and line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-16 12:07:59 +02:00
..
…
…
…
…