Commit Graph
14682 Commits
Author SHA1 Message Date
evangelostsak 921ba61df8 fix(swarm-test): leave the controller's transport alone
It holds a mesh address and routes the pool, but it is the machine running
the play. Pointed at itself over ssh, every task a role delegates to it dials
its own tunnel address instead of staying local -- which is where the second
pass died, on the controller-side NFS mount, after running 189 tasks on the
manager and 176 on each worker over the mesh without a failure.
2026-09-28 08:52:42 +02:00
kevinveenbirkenbach 300f5d1282 Merge pull request #720 from infinito-nexus/dependabot/pip/ruff-0.16.9
chore(deps-dev): bump ruff from 0.16.7 to 0.16.9
2026-09-28 08:11:54 +02:00
dependabot[bot] 16f28ef108 chore(deps-dev): bump ruff from 0.16.7 to 0.16.9
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.16.7 to 0.16.9.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.16.7...0.16.9)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.16.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-28 02:09:38 +00:00
evangelostsak 02c56f6434 test(swarm-test): pin the become password to the account
Asserts it equals users.administrator.password and that it survives into the
deploy-facing twin, which strips the users block it comes from.
2026-09-27 23:56:22 +02:00
evangelostsak 93052c4dec fix(swarm-test): escalate with the password the account was given
user-administrator creates the account from users.administrator.password,
which the development inventory states outright. The value ansible sends for
become is generated per host and never reaches the account, so every sudo
over the mesh was refused. Nothing noticed before because the container
connection escalates to root without a password.

The extras already carry that inventory's users, so the same value now rides
the deploy-facing twin as ansible_become_password.
2026-09-27 23:55:58 +02:00
evangelostsak b16a2fb511 revert(provision): restore the user password generator tests
Follows the generator back to its previous behaviour.
2026-09-27 23:55:21 +02:00
evangelostsak 68ae39a4c9 revert(provision): restore the user password generator
Out of scope for 037: the declared-reference pin was not what the swarm
transport needed, and the credential path should not change for it.
2026-09-27 23:55:10 +02:00
evangelostsak 8c4b294252 docs(037): record that peers are dialled at the underlay address
States why the address is only given where inventory names do not resolve
for every member.
2026-09-27 02:05:21 +02:00
evangelostsak fbf17949d7 test(mesh): cover the endpoint argument
A stated address replaces the name peers dial, an unstated host keeps its
name, and a malformed pair aborts rather than being ignored.
2026-09-27 02:05:00 +02:00
evangelostsak a5826b455b fix(swarm-test): state the lab's underlay addresses to the mesh writer
The nodes are reached by container name over the docker connection, which
only the lab network resolves. The controller is not on it, so it is handed
the addresses default.env already pins for the five hosts.
2026-09-27 02:04:27 +02:00
evangelostsak eb8aa72469 revert(mesh): stop writing peer addresses into /etc/hosts
The endpoint carries the address now, so nothing has to be taught a name.
Mutating the controller's /etc/hosts to make a config resolve was the lab
working around its own naming rather than the mesh stating what it dials.
2026-09-27 02:04:02 +02:00
evangelostsak f8aa7712fa fix(wireguard): probe the MTU against the hub's endpoint
The probe runs before the interface exists, so it has to reach the hub over
the underlay -- the same address the peer stanza dials.
2026-09-27 02:03:53 +02:00
evangelostsak c8401329e0 fix(wireguard): dial the peer's endpoint
Falls back to the hostname, so an inventory written before the endpoint
existed renders exactly as it did.
2026-09-27 02:03:41 +02:00
evangelostsak e98af2abc5 feat(mesh): take the underlay addresses on the command line
--endpoint HOST=ADDRESS, repeatable. A topology whose inventory names do not
resolve on every member states the address once, where the mesh is written,
rather than every member having to be taught the names.
2026-09-27 02:03:29 +02:00
evangelostsak fccd4d8382 feat(mesh): carry the endpoint into every peer entry
The rendered interface needs it; the member's own block does not, since a
host never dials itself.
2026-09-27 02:03:17 +02:00
evangelostsak b0697c08d6 feat(mesh): resolve each member's endpoint when planning
Defaults to the inventory name, which is what peers already dial wherever
those names resolve for every member.
2026-09-27 02:03:05 +02:00
evangelostsak 35572b8c06 feat(mesh): give a member the underlay address peers dial
The requirement's topology pins one per host and calls it the underlay
address; the mesh had no field for it and used the inventory name instead.
2026-09-27 02:02:54 +02:00
evangelostsak d51c477048 revert(apt): drop the package index refresh
Added on a hypothesis the next CI run disproved: the index was never stale,
the candidate version was identical before and after. It fixed nothing and
buys a refresh every deploy already gets where it needs one.
2026-09-27 02:02:30 +02:00
evangelostsak 7a996cd0f5 test(provision): cover the declared password reference
Three cases: the value is pinned from host_vars, its vault node survives, and
a reference to something absent still falls back to a fresh password.
2026-09-27 01:30:59 +02:00
evangelostsak e154ad0b6f fix(provision): pin the value a declared password points at
meta/users.yml states 'password: {{ ansible_become_password }}' to say the
account shares the become secret. Provisioning pinned a fresh random over it,
so the account was created with one secret while become sent another; the
lookup returns the expression as a literal, so skipping the pin would hand
the account the text instead. Resolve the reference against host_vars and pin
that value, vault node included.

Any deploy that reaches a host as the administrator over ssh hits this, not
just the mesh: the container connection escalates to root without a password,
which is why nothing caught it.
2026-09-27 01:30:41 +02:00
evangelostsak f4c336b5a1 test(provision): pin the declared-password behaviour
Covers both halves: the policy reaches the caller with no value, and a stated
password is carried through and draws no random pin.
2026-09-26 23:30:07 +02:00
evangelostsak 92b0d7d3b1 fix(mesh): create the administrator on every swarm node
A worker carries no web-app group, so it resolves to no host-type stage and
never ran user-administrator; the account existed on the manager alone. Once
the transport moves, Ansible logs into every node as that user, and the
workers refused the key outright.
2026-09-26 23:30:01 +02:00
evangelostsak 880de69687 fix(provision): keep the password a role states outright
A declared password is a dict when the role asks for a policy and a string
when it names the value. Only the dict case needs a generated secret, but
both took one, so user-administrator's '{{ ansible_become_password }}' was
overwritten by a random pin: the account was created with one secret while
become sent another.

Nothing caught it because the container connection escalates to root without
a password. The first transport that logs in as the administrator and sudos
for real gets 'Incorrect sudo password' on every host.
2026-09-26 23:29:54 +02:00
evangelostsak 8e75ceadb7 docs(037): describe the rotation inside the seam
Records why the nodes converge before the controller joins, and reopens the
key-rotation criterion: the rotation did sever the run, so the box was ticked
on an assumption rather than on evidence.
2026-09-26 21:29:01 +02:00
evangelostsak 8f047444e0 fix(swarm-test): run the mesh converge inside the seam
Between the mesh write and the controller, so both later steps see one key
set.
2026-09-26 21:28:51 +02:00
evangelostsak 9b883498b0 fix(swarm-test): converge the nodes on the re-minted keys
The rotation gate mirrors one host's host_vars over every other, so the mesh
write that follows mints a fresh keypair for all but the mirror source. The
nodes kept running the keys of the first pass, so the hub turned the
controller away and its handshake gate failed the deploy.

Re-render every node before the controller joins, so the hub authorises the
set the inventory now holds.
2026-09-26 21:28:44 +02:00
evangelostsak 6ef8d350e6 refactor(mesh): make the mesh play serve any inventory
It ran only the wireguard role already, so the same play converges the nodes
and admits the controller; only the inventory differs. The connection comes
from the inventory rather than the play, which is what made it controller-only.
2026-09-26 21:28:30 +02:00
evangelostsak a868045d69 test(mesh): cover the controller in the foreign-entry prune
Mirrors the hub's host_vars onto the controller the way the credential reset
does, then asserts the rerun leaves only the mesh the controller belongs to.
Without the fix the file keeps the hub's data entry, is_hub included.
2026-09-26 19:47:10 +02:00
evangelostsak 981a40785d fix(swarm-test): give the controller the lab name map
Nodes resolve each other through the extra_hosts compose hands them; the
controller is not a container and got no such map, so wg setconf could not
resolve the hub and the swarm interface failed to come up. Pass the same
name-to-address pairs the compose file builds from the topology.
2026-09-26 19:43:38 +02:00
evangelostsak d23a9ceb2e feat(mesh): let the caller supply the peer addresses to resolve
A peer's Endpoint is its inventory hostname. Where the controller cannot
resolve those names itself, the caller passes a name-to-address map and the
play writes it to /etc/hosts before the role renders the interface.
2026-09-26 19:34:40 +02:00
evangelostsak 81c32584e0 fix(mesh): prune the controller's foreign mesh entries too
The prune walks the hosts the inventory groups resolve to, but the controller
joins by name rather than through a group, so it was never visited. Its
mirrored host_vars kept the manager's data-mesh entry -- address 10.100.1.1,
is_hub true -- and the controller brought that interface up and enabled hub
forwarding for a mesh it is not a member of.
2026-09-26 19:28:13 +02:00
evangelostsak 4c168d953c fix(mesh): enable wireguard on the controller play
The controller runs against a one-host inventory, so DEPLOYMENT_MODE derives to
compose and the role skipped itself. The controller then never got an interface,
while the transport switch still moved the inventory onto the mesh -- leaving
the second pass dialling tunnel addresses from a host that was not on the
tunnel, which is where it timed out.
2026-09-26 17:48:55 +02:00
Alejandro Roman b160d07008 fix(lint): annotate ClassVar in test_email and reorder pixelfed playwright hooks 2026-09-26 19:27:31 +04:00
evangelostsak 78409cb642 test(packages): pin the per-manager install arguments
Covers the three cases that matter: apt drops recommends, another manager keeps
its defaults, and a concrete module is left untouched.
2026-09-26 16:19:57 +02:00
evangelostsak 5e590fa52c fix(packages): apply the manager arguments to registry installs
Only calls the registry routes through the host's own package manager get them;
a call that names a concrete module already states what it wants.
2026-09-26 16:19:48 +02:00
evangelostsak b42b6f0018 fix(packages): declare the apt arguments a container install needs
apt installs recommends by default. wireguard-tools recommends the virtual
wireguard-modules, whose only providers are kernel images, so a plain install
pulls a realtime kernel into a container, fails there, and leaves the metapackage
broken -- after which every later apt call on that host exits 100, including the
retries of the task that caused it.

Every other apt call in the repository already passes --no-install-recommends;
the registry was the one path that did not.
2026-09-26 16:17:26 +02:00
Alejandro Roman b11815b80b Merge remote-tracking branch 'upstream/main' into feature/role-stalwart-mail-swarm 2026-09-26 18:07:16 +04:00
Alejandro Roman fadf36e0a6 fix(swarm-stalwart): fix CI issues 2026-09-26 18:05:56 +04:00
kevinveenbirkenbach 55fcf9fb11 Merge pull request #717 from infinito-nexus/update/docker-image-versions-svc-ai-ollama-20260925-ad4953f
update(images): update Docker image versions (svc-ai-ollama)
2026-09-26 15:31:59 +02:00
evangelostsak bc9cad5f92 refactor(wireguard): drop the comment left behind by the install gate
It described a binary path probe that no longer exists in the role.
2026-09-26 15:19:13 +02:00
evangelostsak a95ddf5761 fix(mesh): make apt ready on the controller too
The controller play runs the role on its own, without the constructor, so it
never reached the apt preparation the managed hosts get. It installs from the
same stripped image, so it needs the same index refresh before the role's
first package install.
2026-09-26 15:17:57 +02:00
evangelostsak 7aa0efab8e fix(apt): refresh the package index before the first install
The deploy images delete /var/lib/apt/lists at build time and the package
registry installs without update_cache, so the first apt task of a deploy
resolved against an empty index and picked whatever version the stale .deb
metadata named. Refresh once, where apt is already made ready, so every role
benefits rather than each one carrying its own refresh.
2026-09-26 15:17:48 +02:00
evangelostsak d8b733c615 fix(swarm): bring the mesh up before the dist-upgrade
The role was the first thing to install a package after 'update' dist-upgrades
every node, and apt could no longer satisfy the dependencies -- not for
wireguard-tools, and not for iproute2 or iputils-ping either. Nothing between
the two installs anything, so the role was simply first to hit it.

Ahead of the upgrade is also where the mesh belongs: it is the transport the
rest of the deploy is meant to run over, so it comes up before anything else
touches the host. It stays ahead of the swarm bootstrap for the reason it
always was.
2026-09-26 12:20:14 +02:00
evangelostsak 15c0458110 refactor(swarm-test): call the mesh steps from their own module 2026-09-26 11:14:12 +02:00
evangelostsak 935563f58e refactor(swarm-test): gather the round's mesh steps in one module
Minting the mesh, putting the controller on it and moving the inventory onto
it belong together, and they took matrix.py back over the 500-line cap.
2026-09-26 11:14:12 +02:00
evangelostsak 93e3331780 fix(swarm-test): keep the controller out of the deploy inventory
It joins the mesh through its own play instead.
2026-09-26 11:14:12 +02:00
evangelostsak 68bb44dc55 fix(swarm-test): give the deploy controller its own play
Listing it in the deploy inventory made it a target of playbook.yml, which
runs hosts: all with become: true -- so the controller took the whole host
bootstrap and died on the DNS check, which expects the test domain to resolve
on a managed node.

It is not a managed host. It runs the deploy, and it needs one interface, so
it gets a play that installs exactly that.
2026-09-26 11:13:58 +02:00
evangelostsak 3325a357c7 test(ci): carry the mesh state in the plan fixture 2026-09-26 09:48:20 +02:00
evangelostsak 189c91122c fix(ci): feed the mesh axis to the retrigger
entries_of requires it, so a retrigger would have aborted the same way the
plan did.
2026-09-26 09:48:20 +02:00
evangelostsak c839dd07ff fix(ci): accept the mesh axis in the plan renderer
The discovery step builds its table with plan.py, not matrix.py, so passing
--vpn aborted the chunk before a runner was spent on it. The plan now takes
the flag and shows the state as a column, the way it already shows the onion.
2026-09-26 09:48:09 +02:00