fix(master): block inventory writes from revoked agents (#6)

ApplySnapshot and UpsertService now take a FOR SHARE lock on the agent's
row and refuse with model.ErrRevoked when it is revoked. RevokeAgent's
UPDATE waits on that lock, so an in-flight snapshot or upsert can no
longer bring a revoked agent's services back after the revoke cleanup.
This commit is contained in:
2026-10-12 15:18:51 +02:00
parent 0e9e34db06
commit 2fe9b5ba0d
3 changed files with 72 additions and 3 deletions
@@ -110,3 +110,31 @@ func TestAgents_RevokeRemovesOwnComposeAndContainerServices(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, []string{"gitea_gitea"}, names(got), "swarm rows are shared and must survive a revoke")
}
func TestRevokedAgent_InventoryWritesRefused(t *testing.T) {
s := openTestStore(t)
ctx := context.Background()
id := newAgent(t, s, "node-2")
require.NoError(t, s.RevokeAgent(ctx, id, t0))
assert.ErrorIs(t, s.ApplySnapshot(ctx, id, worker, []protocol.Service{slusk}, t0.Add(time.Minute)), model.ErrRevoked)
assert.ErrorIs(t, s.UpsertService(ctx, id, "", adhoc, t0.Add(time.Minute)), model.ErrRevoked)
got, err := s.ListServices(ctx)
require.NoError(t, err)
assert.Empty(t, got, "a revoked agent must not write inventory")
}
func TestRevokedAgent_UpsertDoesNotResurrectRemovedRow(t *testing.T) {
s := openTestStore(t)
ctx := context.Background()
id := newAgent(t, s, "node-2")
require.NoError(t, s.ApplySnapshot(ctx, id, worker, []protocol.Service{slusk}, t0))
require.NoError(t, s.RevokeAgent(ctx, id, t0.Add(time.Hour)))
assert.ErrorIs(t, s.UpsertService(ctx, id, "", slusk, t0.Add(2*time.Hour)), model.ErrRevoked)
got, err := s.ListServices(ctx)
require.NoError(t, err)
assert.Empty(t, got, "the revoked agent's removed row must stay removed")
}