package host import ( "database/sql" "errors" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "testing" "time" ) func TestPlan14Fix2InventoryReceiptPruned(t *testing.T) { f, root, q := plan14InstallRegistration(t, hostclaim.KindLanding, provision.LandChecking) state := &plan14RegistrationCrash{rollbackDisk: &rollbackDisk{dir: t.TempDir()}} root.Stop.Coordinator.Guard.Root = hostguard.NewRootAuthority(state) if _, err := root.commitRootInventory(t.Context(), q); err == nil { t.Fatal("missing crash") } db, err := sql.Open("sqlite", f.journalPath) if err != nil { t.Fatal(err) } defer db.Close() if _, err = db.Exec("DELETE FROM journal_requests"); err != nil { t.Fatal(err) } if err := root.recoverRootInventory(t.Context()); err != nil { t.Fatal(err) } view, err := root.Stop.Coordinator.Guard.Root.View(t.Context(), q.Target.Host, hostguard.Captured{Incarnation: q.Target.Incarnation, Stamp: q.Target.Stamp}) if err != nil || view.Installation.InventoryRevision != 1 { t.Fatalf("root commit not recovered: %+v %v", view, err) } } func TestPlan14Fix2OldInventoryRequestLeavesNoIntent(t *testing.T) { _, root, q := plan14InstallRegistration(t, hostclaim.KindLanding, provision.LandChecking) q.IssuedAt = time.Now().Add(-time.Hour).UnixNano() if _, err := root.commitRootInventory(t.Context(), q); err == nil { t.Fatal("old request accepted") } pending, err := root.Stop.Coordinator.Guard.Root.PendingInstallations() if err != nil || len(pending) != 0 { t.Fatal("old request fenced host", pending, err) } } func TestPlan14Fix2CommittedInventoryAfterETagChange(t *testing.T) { t.Parallel() f, root, q := plan14InstallRegistration(t, hostclaim.KindLanding, provision.LandChecking) state := &plan14RegistrationCrash{rollbackDisk: &rollbackDisk{dir: t.TempDir()}} root.Stop.Coordinator.Guard.Root = hostguard.NewRootAuthority(state) if _, err := root.commitRootInventory(t.Context(), q); err == nil { t.Fatal("missing crash") } // Change the claim without changing its holder tuple. obs := f.read() obs.Claim.Op.Journal["fix2"] = "changed" if _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Op.Journal["fix2"] = "changed"; return nil }); err != nil { t.Fatal(err) } // The committed request still completes after an ETag-only change. if err := root.recoverRootInventory(t.Context()); err != nil { t.Fatal(err) } _, err := root.Stop.Coordinator.Guard.Root.PendingInstallations() if err != nil && !errors.Is(err, hostguard.ErrFenced) { t.Fatal(err) } } func TestPlan14Fix2SupersededInventoryIntentAbandoned(t *testing.T) { f, root, q := plan14InstallRegistration(t, hostclaim.KindLanding, provision.LandChecking) state := &rollbackDisk{dir: t.TempDir()} root.Stop.Coordinator.Guard.Root = hostguard.NewRootAuthority(state) state.afterWrite = func(string, []byte) { state.afterWrite = nil; _ = root.Reconcile.Registry.Close() } if _, err := root.commitRootInventory(t.Context(), q); err == nil { t.Fatal("closed registry accepted commit") } pending, err := root.Stop.Coordinator.Guard.Root.PendingInstallations() if err != nil || len(pending) != 1 { t.Fatal("missing pending intent", pending, err) } if _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Op.Journal["fix2"] = "changed"; return nil }); err != nil { t.Fatal(err) } if err := root.recoverRootInventory(t.Context()); err != nil { t.Fatal(err) } pending, err = root.Stop.Coordinator.Guard.Root.PendingInstallations() if err != nil || len(pending) != 0 { t.Fatal("superseded intent survived", pending, err) } _, err = root.Stop.Coordinator.Guard.Root.View(t.Context(), q.Target.Host, hostguard.Captured{Incarnation: q.Target.Incarnation, Stamp: q.Target.Stamp}) if !errors.Is(err, hostguard.ErrFenced) { t.Fatal("abandon removed quarantine", err) } }