diff --git a/internal/bootstrap/adopted.go b/internal/bootstrap/adopted.go index 2a1ee1b..ed7207c 100644 --- a/internal/bootstrap/adopted.go +++ b/internal/bootstrap/adopted.go @@ -87,8 +87,11 @@ func guardResources(ports []int) []map[string]any { return []map[string]any{ {"id": guardID, "type": "file", "path": guardPath, "content": AsGuard(ports), "mode": "0644"}, {"id": guardUnitID, "type": "file", "path": guardUnitPath, "content": guardUnitText(), "mode": "0644"}, + // A changed table is reloaded — the unit's ExecReload loads it in one transaction, so the + // ports are never unguarded — and only a changed unit restarts it. As the controller + // declares it, so the first push finds nothing different. {"id": guardRunningID, "type": "service", "unit": guardUnit, "state": "running", - "boot": "enabled", "restart-on": []any{guardID, guardUnitID}}, + "boot": "enabled", "reload-on": []any{guardID}, "restart-on": []any{guardUnitID}}, } } diff --git a/internal/bootstrap/adopted_test.go b/internal/bootstrap/adopted_test.go index cbe3a98..f52e0e1 100644 --- a/internal/bootstrap/adopted_test.go +++ b/internal/bootstrap/adopted_test.go @@ -121,7 +121,10 @@ func TestAnAdoptedBundleLoadsNoDroppingTableAndExactlyTheGuard(t *testing.T) { t.Error("nft, which loads the guard, is no longer installed") } unit := r.Declaration.Resources[at[guardRunningID]].(*declaration.Service) - if unit.Unit != guardUnit || unit.State != "running" || strings.Join(unit.RestartOn, ",") != guardID+","+guardUnitID { + // A changed table is reloaded, never restarted: a restart deletes the table before loading + // it again, leaving the ports unguarded in between. + if unit.Unit != guardUnit || unit.State != "running" || strings.Join(unit.RestartOn, ",") != guardUnitID || + strings.Join(unit.ReloadOn, ",") != guardID { t.Errorf("the guard's service: %+v", unit) } stop := r.Declaration.Resources[at[guardUnitID]].(*declaration.File).Content