Watch
0
0
Fork
You've already forked hyperhive
0

swarm-archive: forget future-dated snapshots before each prune; fix the grants case

A client chooses a snapshot's time, so snapshots dated ahead could fill
--keep-last and push the real ones out of a prune. The prune now forgets
snapshots dated more than an hour after now first, and logs their ids. The
VM test forges one and checks the real snapshot stays. The bao-grants case
that read a + path as a glob now names the literal path, and two cases that
pinned 'nothing else' list the archive grants. The ciRunner stanza is back
at its original indent.
This commit is contained in:
atlas 2026-11-10 08:32:54 +01:00
commit 793d987a88

View file

@ -6,7 +6,8 @@
# - a backup of two agents lands in one repository each, and `work/` is left out;
# - one agent's login cannot read another's repository;
# - a client cannot delete or overwrite anything, and the server-side prune
# applies the retention;
# applies the retention, also when a client has forged a snapshot dated in
# the future;
# - the users file follows the store: a user removed from it is refused without
# a restart of the server;
# - a hive that has agents and no grant backs up nothing and succeeds.
@ -146,9 +147,9 @@ pkgs.testers.runNixOSTest {
../host-modules/swarm-archive.nix
];
# Keep only the newest snapshot, so the prune is visible after two backups.
# The newest snapshot, plus whatever is younger than three days.
services.hyperhive.deploy.archive.retention = {
keepWithin = null;
keepWithin = "3d";
keepLast = 1;
};
@ -252,8 +253,8 @@ pkgs.testers.runNixOSTest {
with subtest("a client cannot delete or overwrite"):
snap = machine.succeed(restic("alpha", "alpha", "snapshots --json")).split('"id":"')[1].split('"')[0]
# The presence control: a lock, which a backup creates and removes, is deletable.
machine.succeed(restic("alpha", "alpha", "backup --host alpha ${agentRoot}/alpha/state"))
# An old snapshot for the prune to drop. The backup is also the presence control: a lock, which a backup creates and removes, is deletable.
machine.succeed(restic("alpha", "alpha", "backup --host alpha --time '2000-01-01 00:00:00' ${agentRoot}/alpha/state"))
assert status("alpha", "alpha", f"snapshots/{snap}", "DELETE") == "403"
assert status("alpha", "alpha", "config", "POST") == "403"
machine.fail(restic("alpha", "alpha", "forget --keep-last 1 --prune"))
@ -263,6 +264,19 @@ pkgs.testers.runNixOSTest {
machine.succeed("systemctl start swarm-archive-prune")
for agent in agents:
assert machine.succeed(restic(agent, agent, "snapshots --json")).count('"id"') == 1
# The old snapshot went, the one from the hive's backup stayed.
assert snap in machine.succeed(restic("alpha", "alpha", "snapshots --json"))
with subtest("a snapshot dated in the future cannot push the real ones out of the retention"):
# restic counts --keep-within from the newest snapshot, and the client sets its time.
machine.succeed(restic("alpha", "alpha", "backup --host alpha --time '2100-01-01 00:00:00' ${agentRoot}/alpha/state"))
assert machine.succeed(restic("alpha", "alpha", "snapshots --json")).count('"id"') == 2
machine.succeed("systemctl start swarm-archive-prune")
left = machine.succeed(restic("alpha", "alpha", "snapshots --json"))
assert left.count('"id"') == 1 and snap in left, left
machine.succeed("journalctl -u swarm-archive-prune | grep -q 'archive prune warning: alpha has snapshots dated after now'")
# beta's repository had nothing forged, and says nothing.
machine.fail("journalctl -u swarm-archive-prune | grep -q 'archive prune warning: beta'")
with subtest("a user removed from the store is refused without a restart"):
machine.succeed(f"rm -r {store}/secret/swarm/services/archive/users/beta")