fix: an apply that starts etcd records it, and the start enables wireguard (keel#188) #191

Merged
navigator merged 3 commits from fix/etcd-started-by-apply-188 into main 2026-10-16 11:25:05 -03:00
Owner

Fixes #188. Stacked on #190 → #189 → #187 → #185: merge those first.

Found by the cross-site rehearsal of 2026-10-16 (3 cloud advanced CTs, BR1 and BR2). The first boot specs had overlays.wireguard: disabled (no network.overlay before the join).

  1. At the third join, keel.mesh.etcd.start wrote overlays.etcd: enabled into a spec that still said wireguard: disabled. etcd requires wireguard, so the spec did not validate and etcd did not start.
  2. The operator then enabled both and applied. etcd started, but nothing wrote /var/lib/keel/etcd/started, and every later apply refused the member: "holds a member keel never started".

What changes:

  • start() enables the wireguard overlay with etcd. The join already wrote network.overlay.
  • plan_etcd writes the started mark on the live system when the cluster is known and etcd holds no member yet, as the join and the form do. A member directory that keel did not start is still refused.

Tests: tests/test_mesh_etcd.py (the start enables wireguard), tests/test_system_etcd.py (the mark on a live apply, none under a root, a member keel never started still refused).

keel 0.23.37.

Fixes #188. Stacked on #190 → #189 → #187 → #185: merge those first. Found by the cross-site rehearsal of 2026-10-16 (3 cloud advanced CTs, BR1 and BR2). The first boot specs had `overlays.wireguard: disabled` (no `network.overlay` before the join). 1. At the third join, `keel.mesh.etcd.start` wrote `overlays.etcd: enabled` into a spec that still said `wireguard: disabled`. etcd requires wireguard, so the spec did not validate and etcd did not start. 2. The operator then enabled both and applied. etcd started, but nothing wrote `/var/lib/keel/etcd/started`, and every later apply refused the member: "holds a member keel never started". What changes: - `start()` enables the wireguard overlay with etcd. The join already wrote `network.overlay`. - `plan_etcd` writes the `started` mark on the live system when the cluster is known and etcd holds no member yet, as the join and the form do. A member directory that keel did not start is still refused. Tests: `tests/test_mesh_etcd.py` (the start enables wireguard), `tests/test_system_etcd.py` (the mark on a live apply, none under a root, a member keel never started still refused). keel 0.23.37.
The reseed probe sums pg_database_size() over every database as the
role repl. That function needs CONNECT on the database. The keel-lnpp
check database revoked CONNECT from PUBLIC, and every reseed of the
LNPP pair refused: "permission denied for database keel_check".

The netns test of the pair now revokes CONNECT on the shop database
from PUBLIC before case (d), as the check database did. The unit tests
ask that the probe measures only the databases repl may connect to,
and that a refusal for lack of space names the databases it left out.
pg_database_size() needs CONNECT on the database or the role
pg_read_all_stats. An application database with REVOKE CONNECT FROM
PUBLIC stopped every reseed with "permission denied".

The probe now sums the size with a FILTER on
has_database_privilege(oid, 'CONNECT') and counts the databases it
leaves out. The free space check uses the size it has. When the check
refuses, the message says how many databases the need leaves out. The
base backup itself is not changed: it copies the whole cluster.

pg_read_all_stats is not granted to repl: it gives more read access
than the reseed needs.
chore: keel 0.23.33 (keel#183)
All checks were successful
security / security (pull_request) Successful in 1m35s
tests / etcd (pull_request) Successful in 6m27s
tests / vip (pull_request) Successful in 7m14s
tests / mesh (pull_request) Successful in 11m7s
tests / etcd-auth (pull_request) Successful in 5m7s
tests / package (pull_request) Successful in 8s
tests / vip-upgrade (pull_request) Successful in 7m41s
tests / mariadb-simple (pull_request) Successful in 5m16s
tests / mariadb (pull_request) Successful in 7m12s
tests / tests (pull_request) Successful in 18m41s
tests / postgres (pull_request) Successful in 11m17s
a6271bbc9b
On a pair, keel-overlay-anubis-key.service makes no key on the database
replica: the key is shared and comes from the primary (decision 0041).
Nothing copied it, so anubis@keel did not start there, and the apply
failed the anubis overlay. The LNMP and LNPP pair tests copied the key
by hand (Keel-Linux/common#56).

The tests ask that the other member of the pair record, and no other
member, gets the key over the members' channel; that a paired replica
with no key asks for it in a run that may change the machine, and
writes it with mode 0600 before Anubis starts; that a replica in no
pair yet starts nothing and says why; and that an unreachable primary
refuses the step.
The secret message of the members' channel gives a second name,
anubis/signing_key, answered with /etc/anubis/keel.key only to the
other member of the pair record, as the replication credential is.

keel.system.anubiskey observes, on the live system and only in a run
that may change the machine, a node whose spec makes it a database
replica, with Anubis enabled, no key file and no
secrets.anubis_signing_key declared. It asks the other member of its
pair, and the plan writes the key (mode 0600) before the overlay's
units start. The key unit then finds the key and leaves it.

Before the pair record exists, the apply starts nothing for Anubis and
says why, so the first boot of a replica no longer fails the overlay. A
key that is there is never replaced.
chore: keel 0.23.34 (common#56)
Some checks failed
security / security (pull_request) Failing after 1m40s
tests / etcd (pull_request) Successful in 7m8s
tests / mesh (pull_request) Successful in 11m45s
tests / vip (pull_request) Successful in 7m5s
tests / tests (pull_request) Has been cancelled
tests / vip-upgrade (pull_request) Has been cancelled
tests / etcd-auth (pull_request) Has been cancelled
tests / mariadb (pull_request) Has been cancelled
tests / package (pull_request) Has been cancelled
tests / mariadb-simple (pull_request) Has been cancelled
tests / postgres (pull_request) Has been cancelled
8fc10142ee
keel-vip.service starts only when /var/lib/keel/etcd/cluster.json
exists. On a cloud simple pair, keel diff reported overlays.vip as
drift (enabled and inactive) on every run, and each apply printed a
start of the unit that did nothing.

The tests ask that, before the cluster exists, diff compares only
whether the unit is enabled and says why it does not run, and that
apply enables the unit and does not start it. With the cluster, an
inactive controller is drift and apply starts it, as before.
Before etcd's cluster.json exists (always in cloud simple, and in
cloud advanced until etcd forms), the start condition of
keel-vip.service holds it. keel diff now compares only whether the
unit is enabled, and gives the reason: without etcd, keel vip check
(keel-vip-check.timer) does the VIP's work. keel spec apply enables the
unit, does not start it, and says why. With the cluster, both compare
and converge the unit as before.
chore: keel 0.23.35 (keel#106)
Some checks are pending
security / security (pull_request) Waiting to run
tests / tests (pull_request) Waiting to run
tests / mesh (pull_request) Waiting to run
tests / etcd (pull_request) Waiting to run
tests / vip (pull_request) Waiting to run
tests / vip-upgrade (pull_request) Waiting to run
tests / etcd-auth (pull_request) Waiting to run
tests / mariadb (pull_request) Waiting to run
tests / mariadb-simple (pull_request) Waiting to run
tests / postgres (pull_request) Waiting to run
tests / package (pull_request) Waiting to run
093da566b6
On a cut MariaDB primary, a write that waits for the replica's ACK
returned OK when the semi-synchronous timeout (10 s) ended, before the
old primary dropped the VIP (15 s). The fence did not stop it either:
SET GLOBAL read_only = ON waits for the backup lock behind that write.
So writes that the old primary acknowledged were lost at a failover.

tests/test_mariadb_fence_real.py runs two real mariadbd, semi-
synchronous with AFTER_SYNC, the replica stopped by SIGSTOP. The fence
must end with read_only on, well within the timeout, with an error and
no OK for the waiting write, and with the replica's dump thread kept.
The timeout must be at least twice the VIP's release. The job
"mariadb / trixie" runs it as root.
keel database follow turns a node read only that took writes when it
loses the VIP. It now sets read_only in a client of its own and, while
that waits for the backup lock, kills every client connection (not the
server's own threads, not the replication threads). A write that waited
for the replica's ACK gets an error, not OK. Its row stays on the old
primary as an errant transaction, so the node comes back diverged and
waits for the confirmed reseed, as before.

rpl_semi_sync_master_timeout goes from 10 s to 30 s: longer than the
VIP's release (15 s, keel#174) and the follow it starts, with room. A
lost replica still gives the fallback to asynchronous commits, 30 s
after the last ACK in place of 10 s. This changes the 10 s of the
maintainer's answer of 2026-10-03 (decision 0031): the release test's
criterion is no acknowledged write lost at a cut of the primary, and
10 s cannot meet it while the release is 15 s.
chore: keel 0.23.36 (keel#186)
Some checks are pending
security / security (pull_request) Waiting to run
tests / tests (pull_request) Waiting to run
tests / mesh (pull_request) Waiting to run
tests / etcd (pull_request) Waiting to run
tests / vip (pull_request) Waiting to run
tests / vip-upgrade (pull_request) Waiting to run
tests / etcd-auth (pull_request) Waiting to run
tests / mariadb (pull_request) Waiting to run
tests / mariadb-simple (pull_request) Waiting to run
tests / postgres (pull_request) Waiting to run
tests / package (pull_request) Waiting to run
c2fc854a51
The cross-site rehearsal of 2026-10-16 booted 3 cloud advanced CTs with
overlays.wireguard and overlays.etcd disabled. At the third join, the
start of etcd wrote overlays.etcd: enabled into a spec that still said
wireguard: disabled, which does not validate, and etcd did not start.
The operator then enabled both and applied: etcd started, but nothing
wrote /var/lib/keel/etcd/started, and every later apply refused the
member as one keel never started.

The tests ask that the start enables wireguard with etcd, and that an
apply that starts etcd for the cluster of cluster.json records it.
keel.mesh.etcd.start sets overlays.wireguard: enabled with
overlays.etcd: enabled. etcd requires wireguard, and the join already
wrote network.overlay.

plan_etcd writes /var/lib/keel/etcd/started on the live system when
the cluster is known and etcd holds no member yet, as the join and the
form do when they start etcd. A member directory that keel did not
start is still refused.
chore: keel 0.23.37 (keel#188)
Some checks are pending
security / security (pull_request) Waiting to run
tests / tests (pull_request) Waiting to run
tests / mesh (pull_request) Waiting to run
tests / etcd (pull_request) Waiting to run
tests / vip (pull_request) Waiting to run
tests / vip-upgrade (pull_request) Waiting to run
tests / etcd-auth (pull_request) Waiting to run
tests / mariadb (pull_request) Waiting to run
tests / mariadb-simple (pull_request) Waiting to run
tests / postgres (pull_request) Waiting to run
tests / package (pull_request) Waiting to run
ebbd32bf73
navigator force-pushed fix/etcd-started-by-apply-188 from ebbd32bf73
Some checks are pending
security / security (pull_request) Waiting to run
tests / tests (pull_request) Waiting to run
tests / mesh (pull_request) Waiting to run
tests / etcd (pull_request) Waiting to run
tests / vip (pull_request) Waiting to run
tests / etcd-auth (pull_request) Waiting to run
tests / vip-upgrade (pull_request) Waiting to run
tests / mariadb (pull_request) Waiting to run
tests / postgres (pull_request) Waiting to run
tests / package (pull_request) Waiting to run
tests / mariadb-simple (pull_request) Waiting to run
to dd4f7bdbf9
Some checks failed
tests / etcd (pull_request) Successful in 6m45s
tests / vip (pull_request) Successful in 6m44s
tests / mesh (pull_request) Successful in 11m17s
tests / postgres (pull_request) Successful in 10m42s
tests / etcd-auth (pull_request) Successful in 4m58s
tests / package (pull_request) Successful in 8s
tests / mariadb-simple (pull_request) Successful in 4m47s
tests / vip-upgrade (pull_request) Successful in 7m42s
tests / mariadb (pull_request) Successful in 7m18s
tests / tests (pull_request) Successful in 20m24s
security / security (pull_request) Failing after 1m26s
2026-10-16 07:48:09 -03:00
Compare
navigator force-pushed fix/etcd-started-by-apply-188 from dd4f7bdbf9
Some checks failed
security / security (pull_request) Failing after 1m26s
tests / etcd (pull_request) Successful in 6m45s
tests / vip (pull_request) Successful in 6m44s
tests / mesh (pull_request) Successful in 11m17s
tests / etcd-auth (pull_request) Successful in 4m58s
tests / package (pull_request) Successful in 8s
tests / mariadb-simple (pull_request) Successful in 4m47s
tests / vip-upgrade (pull_request) Successful in 7m42s
tests / mariadb (pull_request) Successful in 7m18s
tests / tests (pull_request) Successful in 20m24s
tests / postgres (pull_request) Successful in 10m42s
to 09b8c76bc4 2026-10-16 07:55:01 -03:00
Compare
navigator force-pushed fix/etcd-started-by-apply-188 from dd4f7bdbf9
Some checks failed
security / security (pull_request) Failing after 1m26s
tests / etcd (pull_request) Successful in 6m45s
tests / vip (pull_request) Successful in 6m44s
tests / mesh (pull_request) Successful in 11m17s
tests / etcd-auth (pull_request) Successful in 4m58s
tests / package (pull_request) Successful in 8s
tests / mariadb-simple (pull_request) Successful in 4m47s
tests / vip-upgrade (pull_request) Successful in 7m42s
tests / mariadb (pull_request) Successful in 7m18s
tests / tests (pull_request) Successful in 20m24s
tests / postgres (pull_request) Successful in 10m42s
to 1a5aa16f3a
All checks were successful
security / security (pull_request) Successful in 1m45s
tests / etcd (pull_request) Successful in 7m4s
tests / vip (pull_request) Successful in 6m56s
tests / mesh (pull_request) Successful in 11m15s
tests / vip-upgrade (pull_request) Successful in 8m10s
tests / package (pull_request) Successful in 9s
tests / etcd-auth (pull_request) Successful in 6m17s
tests / mariadb-simple (pull_request) Successful in 7m19s
tests / mariadb (pull_request) Successful in 10m9s
tests / tests (pull_request) Successful in 21m17s
tests / postgres (pull_request) Successful in 12m59s
2026-10-16 10:04:21 -03:00
Compare
Sign in to join this conversation.
No description provided.