Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 68680e863b fix(web-app-zammad): gate init on the search precheck in swarm too
`docker stack deploy` ignores `depends_on`, so in swarm `init` started
even when the `precheck` service exited 1. The stack then looked exactly
as it did before #982: `init` hung, and `stack_ready.sh` reported "not
converged" until all 600 retries of the convergence handler ran out.
The single failed `precheck` task never turned fatal, because
`has_fatal_task` needs three errors.

`init` now runs `search_allocation_precheck.sh` in front of
`zammad-init` as well. In compose the `precheck` service still stops
`compose up` before `init` starts. In swarm, `init` keeps
`restart: on-failure`, so a refusing Elasticsearch makes its tasks fail
again and again. After three errors past `swarm_stack_fatal_grace`
(180 s), `has_fatal_task` marks the service stuck and the deploy stops
with the precheck output in the task report.

Reported by CodeRabbit on #982.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:33:53 +02:00
kevinveenbirkenbachandClaude Opus 5.5 fcf1cca129 fix(web-app-zammad): stop the deploy when Elasticsearch allocates no shard
On a data disk with less free space than the high disk watermark asks
for, Elasticsearch creates indices but assigns none of their primary
shards. `zammad-init` then reports every search index update as failed
and never exits, so `compose up` waits on
`service_completed_successfully` until its 1800 s task timeout and the
deploy fails without naming the cause (#850). Elasticsearch's default
high watermark is an absolute 150 GB headroom on a large disk.

A new one-shot `precheck` service runs `search_allocation_precheck.sh`
with `restart: "no"`, and `init` depends on it with
`service_completed_successfully`:
- it waits a bounded time for `_cluster/health`;
- it creates a one-shard, zero-replica probe index with
  `wait_for_active_shards=1`;
- once Elasticsearch allocates it, it deletes the probe and exits 0;
- otherwise it prints the creation answer and the answer of
  `_cluster/allocation/explain` and exits 1.

The check sits in its own service because `init` keeps
`restart: on-failure`: wrapped into `init`, the failing check was
restarted seven times within eight minutes on a local deploy while
`compose up` kept waiting. Inside the stack, a `restart: "no"`
dependency that exits 1 made `compose up` fail after one second with
`service "pre" didn't complete successfully: exit 1`.

Every curl call carries --connect-timeout and --max-time, so a hanging
request cannot outlast the bounded wait. The image's entrypoint passes
any non-`zammad-*` command through `exec "$@"` (bin/docker-entrypoint of
7.1.2), and the image ships `curl`. The watermark values themselves come
with feature/store-i18n (b82591ba9d); this change covers inventories
without a variant cap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:33:53 +02:00
kevinveenbirkenbach a7a92e94f6 Merge pull request #992 from evangelostsak/fix/tests/epel-mdapi-probe
fix(tests): read EPEL availability from mdapi instead of a mirror listing
2026-10-18 11:37:15 +02:00
kevinveenbirkenbach 7afe8f014b Merge pull request #983 from infinito-nexus/fix/web-app-zammad/search-disk-watermarks
fix(web-app-zammad): size Elasticsearch disk watermarks to the disk and cap them in CI and dev
2026-10-18 11:33:04 +02:00
kevinveenbirkenbach 9998d9943d Merge pull request #988 from infinito-nexus/update/docker-image-versions-web-app-pixelfed-20261017-9c4fbcd
update(images): update Docker image versions (web-app-pixelfed)
2026-10-18 11:29:52 +02:00
evangelostsak 7a1c6651a5 docs(tests): drop the EPEL mirror comment
The probe reads one host either way. The mirror history belongs to the
commit that moved it.
2026-10-18 01:24:59 +02:00
evangelostsak d8613a842c fix(tests): read EPEL availability from mdapi instead of a mirror listing
The probe scraped a directory listing under dl.fedoraproject.org, which
redirects to whichever mirror is near the caller. A mirror that does not
carry the directory answers 404, and 404 is not retried, so every EPEL
package came back uncheckable and the suite failed on runners whose nearest
mirror is incomplete -- twice in a row, while the same URLs answer 200 from
elsewhere.

mdapi already backs the Fedora probe and serves the epel10 branch from a
single host. Routing EPEL through it removes the mirror roulette and keeps
the verdict sharp: a name it does not know is still reported absent rather
than unknown.

Checked against the live branch: ShellCheck and keepassxc resolve, an
invented name reports absent, and the suite passes.
2026-10-18 01:24:59 +02:00
evangelostsak a6bf8f6d49 chore(swarm-test): stop forwarding INFINITO_SWARM_TRANSPORT_BENCH 2026-10-18 01:01:10 +02:00
evangelostsak 4a100cade8 chore(ci): stop handing the swarm round INFINITO_SWARM_TRANSPORT_BENCH 2026-10-18 01:01:10 +02:00
evangelostsak bc4db9fb9f docs(ci): drop the INFINITO_SWARM_TRANSPORT_BENCH variable 2026-10-18 01:01:10 +02:00
evangelostsak 97afaed757 chore(env): drop INFINITO_SWARM_TRANSPORT_BENCH 2026-10-18 01:01:10 +02:00
evangelostsak 4a112c1295 chore(env): stop passing INFINITO_SWARM_TRANSPORT_BENCH through 2026-10-18 01:01:10 +02:00
evangelostsak 3fd827508f chore(swarm-test): delete the transport bench playbook 2026-10-18 01:01:10 +02:00
evangelostsak 1e080c1a2e refactor(swarm-test): drop the transport bench from the mesh prologue 2026-10-18 01:01:10 +02:00
evangelostsak 5f945c2991 refactor(swarm-test): run the mesh playbooks from playbooks/ and drop the transport bench 2026-10-18 01:01:10 +02:00
evangelostsak 24a07325f8 test(wireguard): run the mesh playbook from playbooks/ 2026-10-18 01:01:09 +02:00
evangelostsak 4096c6e553 refactor(playbooks): move and rename the swarm lab's passwordless sudo playbook 2026-10-18 01:01:09 +02:00
evangelostsak 5195326ba7 refactor(playbooks): move the mesh playbook into playbooks/ 2026-10-18 01:01:09 +02:00
evangelostsak 8da87a524f feat(playbooks): load the root group_vars for playbooks under playbooks/ 2026-10-18 01:01:09 +02:00
evangelostsak 01ef83217b docs(readme): list WireGuard as beta with its current description 2026-10-18 01:01:09 +02:00
evangelostsak bdfb025229 refactor(user-administrator): drop the static sudoers file 2026-10-18 01:01:09 +02:00
evangelostsak 7250442c38 refactor(user-administrator): use the administrator name constant 2026-10-18 01:01:09 +02:00
evangelostsak a8bf405fa3 refactor(user-administrator): render the sudoers drop-in from the name 2026-10-18 01:01:09 +02:00
evangelostsak 7b27a46556 refactor(user-administrator): name the administrator once 2026-10-18 01:01:09 +02:00
evangelostsak ea94695ec9 refactor(constructor): leave the mesh administrator to svc-net-wireguard 2026-10-18 01:01:08 +02:00
evangelostsak aea4373486 refactor(wireguard): create the administrator the mesh transport logs in as 2026-10-18 01:01:08 +02:00
evangelostsak 77e6588b36 refactor(wireguard): gate the core and the skip on the named compose case 2026-10-18 01:01:08 +02:00
evangelostsak a009f64b15 refactor(wireguard): name the compose host the mesh CLI gave no mesh 2026-10-18 01:01:08 +02:00
evangelostsak ebb6561972 test(wireguard): read the host image from the role's test service 2026-10-18 01:01:08 +02:00
evangelostsak 5ccf9551da feat(wireguard): declare the CLI test's host image and move the role to beta 2026-10-18 01:01:08 +02:00
kevinveenbirkenbachandClaude Opus 5.5 088c8be14f fix(ci): pull workspace images through the GHCR mirror like the deploy jobs
The workspace jobs pulled every image anonymously from Docker Hub and
two of them failed on its rate limit in run 39059128678 (#990): the
fedora swarm drill on busybox:1.38 on all three nodes, the centos
compose drill while building tor-smtp_custom. The regular deploy jobs
pull through the GHCR mirror, but the mirror was tied to being a real
GitHub runner: Profile.image_mirror_enabled() returned
runs_on_github(), and the swarm routine generated mirrors.yml only when
cli.meta.runtime reported github, which is act inside make
swarm-zombie.

The mirror is now its own capability, INFINITO_GHCR_MIRROR (next to
INFINITO_GHCR_MIRROR_PREFIX), written as a run fact like
INFINITO_CACHE_STACK: true on a real GitHub runner,
false everywhere else, and a non-empty exported value wins for the run.
Profile.image_mirror_enabled() reads the declaration and keeps
runs_on_github() as the fallback, and 02_provision_inventory.sh gates
on the same value instead of the runtime name. make swarm-zombie
forwards it into act through ACT_ENV, since act ignores the host env.

call-test-workspace.yml declares INFINITO_GHCR_MIRROR=true and passes
INFINITO_GHCR_MIRROR_PREFIX into the workspace container. Nothing else
of the workspace changes: is_ci, runs_on_github, the cache stack and
the docker root stay local-like. The step that loads the rescue paths
now removes the .env it generated on the runner, because on debian
rows load.sh sourced it inside the container and the run inherited
INFINITO_RUNNING_ON_GITHUB=true.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 22:40:13 +02:00
evangelostsak 0c9ad653f7 Merge remote-tracking branch 'upstream/main' into feature/svc-net-wireguard 2026-10-17 22:19:57 +02:00
kevinveenbirkenbach 5f74f05f65 Merge remote-tracking branch 'origin/main' into update/docker-image-versions-web-app-pixelfed-20261017-9c4fbcd 2026-10-17 21:20:50 +02:00
kevinveenbirkenbach f385bf769b Merge remote-tracking branch 'origin/main' into update/docker-image-versions-web-app-pihole-20261017-6082d2d 2026-10-17 21:20:42 +02:00
kevinveenbirkenbach fd4d186259 Merge remote-tracking branch 'origin/main' into fix/web-app-zammad/search-disk-watermarks 2026-10-17 20:41:17 +02:00
kevinveenbirkenbach 41e3146197 Merge remote-tracking branch 'origin/main' into feat/fuzz/yaml-bootstrap-differential 2026-10-17 20:41:07 +02:00
infinito-nexus-updater[bot] 4aef6ec891 update(images): update Docker image versions (web-app-pixelfed) 2026-10-17 18:40:29 +00:00
infinito-nexus-updater[bot] 0da5743f47 update(images): update Docker image versions (web-app-pihole) 2026-10-17 18:40:23 +00:00
kevinveenbirkenbach ff42b6bf3b Merge pull request #985 from infinito-nexus/perf/cache/spot-nested-render
perf(cache): let nested lookups reuse the unrendered applications view
2026-10-17 20:40:22 +02:00
kevinveenbirkenbach 1aeec53fbb Merge remote-tracking branch 'origin/main' into feat/fuzz/yaml-bootstrap-differential 2026-10-17 20:38:29 +02:00
kevinveenbirkenbach 161f65ff5f Merge remote-tracking branch 'origin/main' into perf/cache/spot-nested-render 2026-10-17 20:38:24 +02:00
kevinveenbirkenbach 0f5952636b Merge remote-tracking branch 'origin/main' into fix/web-app-zammad/search-disk-watermarks 2026-10-17 20:38:11 +02:00
kevinveenbirkenbachandClaude Opus 5.5 0cb8bae88d perf(cache): let nested lookups reuse the unrendered applications view
Every cold render of the applications SPOT walks the templates of all
269 roles, and those templates call lookup('config'), lookup('tls'),
lookup('domain') and others. Each of these re-enters
get_merged_applications while the render guard is set, and each one
rebuilt the unrendered view from scratch: a deepcopy of all role
defaults plus the override merge, about 8 ms, never cached. A local
cold render of the development inventory made 351 such calls, 2.82 s
of a 6.86 s render.

The outer render now keeps a copy of its unrendered view and its cache
key on the render guard, taken before the inventory volumes are
popped, so it is the same view a nested call used to build. A nested
call with the same key returns it; any other key still builds its own.
The guard drops it when the render ends.

Measured on the same inventory: the nested calls took 0.07 s instead
of 2.82 s and the cold render 3.60 s instead of 6.86 s. The rendered
SPOT is unchanged: the old and new dumps differ in 69 user passwords,
which the development inventory regenerates on every read (two runs of
the old code differ in 68), and in one absolute checkout path, the only
other difference.
In CI each cold render took 40 to 77 s and the heaviest swarm cell of
#950 missed its budget by a few minutes (#984).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 19:52:34 +02:00
kevinveenbirkenbach fb5a2a3580 Merge pull request #950 from infinito-nexus/update/docker-image-versions-web-app-nextcloud-20261014-c2bc2c1
update(images): update Docker image versions (web-app-nextcloud)
2026-10-17 19:40:12 +02:00
kevinveenbirkenbachandClaude Opus 5.5 a598b67d75 feat(fuzz): fuzz the bootstrap YAML reader against PyYAML weekly
OpenSSF Scorecard reports no fuzzer integration (code scanning alert
507, issue #853). tests/fuzz/_yaml_bootstrap.py is an atheris harness
that feeds arbitrary input to utils.yaml_bootstrap.load_block and fails
whenever the reader accepts a document that load_yaml_str parses
differently or rejects. That is the contract the module's docstring
states, and the existing property test only samples it. The leading
underscore keeps unittest discovery from importing it, because atheris
is only installed with the fuzz extra.

cron-security-fuzz.yml runs the harness for 15 minutes every Sunday and
on demand, on ubuntu-latest, and uploads crashing inputs as the
fuzz-crashes artifact. atheris 3.1.0 ships x86_64 manylinux wheels for
CPython 3.12 to 3.14 only, so it lives in its own `fuzz` extra and
stays out of every other install path. tests/fuzz/ is not part of
`make test`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 18:46:26 +02:00
kevinveenbirkenbachandClaude Opus 5.5 32740d860f fix(utils): stop the bootstrap YAML reader from accepting what PyYAML rejects
utils/yaml_bootstrap.py promises that load_block(text) either equals
yaml.safe_load(text) or raises BootstrapYamlError. A fuzzer comparing
both found two inputs that broke the promise:

- str.splitlines() also breaks lines at \x0b, \x0c and \x1c to \x1e,
  which PyYAML's reader rejects as non-printable. "\x1e/: R" came back
  as {"/": "R"}, and a non-printable character inside a comment line was
  skipped with the comment.
- An indented " ---" on line 1 was taken as the document marker, while
  PyYAML reads it as a plain scalar and rejects the mapping that follows.

load_block now refuses any character outside PyYAML's own printable
class before it splits lines, and takes "---" as the document marker
only at column 0. The line breaks splitlines() still honours (\r, \r\n,
NEL, LS, PS) are line breaks for PyYAML too, so splitting stays as is.
The eight inputs join STRUCTURES in the divergence test; against the
previous reader, four of them diverge and fail it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 18:46:16 +02:00
kevinveenbirkenbachandClaude Opus 5.5 e0a05051b0 fix(web-app-zammad): size Elasticsearch disk watermarks to the disk and cap them in CI and dev
The bundled Elasticsearch ran with its default watermarks. On a large
disk the high watermark's max_headroom demands 150 GB free, so a 1.8 TB
development host with about 56 to 112 GB free assigned no shard,
zammad-init never finished and the compose-up handler timed out after
1800 seconds (#850).

meta/services.yml declares the watermarks on the search service under
search.watermark: 85 / 90 / 95 % of the disk with max_headroom caps of
200 / 150 / 100 GB, which are Elasticsearch's own defaults, so a
production inventory keeps today's behaviour. Every entry in
meta/variants.yml lowers only the caps to 10 / 5 / 2 GB. Variants reach
development and CI inventories, and a production inventory only when
inventory provision is called with --app-variants;
utils/cache/applications.py
deep-merges them onto the base config, so low, high and flood_stage stay
on the base values. compose.yml.j2 passes all six settings to the
container through ZAMMAD_SEARCH_WATERMARK.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 16:26:00 +02:00
kevinveenbirkenbach e154ef48fe Merge pull request #981 from infinito-nexus/update/docker-image-versions-web-app-socialhome-20261017-1ee0b16
update(images): update Docker image versions (web-app-socialhome)
2026-10-17 13:28:46 +02:00
infinito-nexus-updater[bot] ee8e040502 update(images): update Docker image versions (web-app-socialhome) 2026-10-17 10:07:07 +00:00
kevinveenbirkenbach 7a29505353 Merge pull request #979 from infinito-nexus/fix/web-app-openproject/read-only-migration-gate
fix(web-app-openproject): probe migrations without creating tables
2026-10-17 12:03:53 +02:00