package host import ( "context" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/release" ) func TestPlan14GenerationRetirementRetainedRebind(t *testing.T) { for _, change := range []string{"none", "open-writers", "unsettled", "wrong-installation", "wrong-incarnation", "wrong-stamp", "wrong-account", "wrong-enrollment"} { t.Run(change, func(t *testing.T) { f, disk, _ := generationLaunchFixture(t) if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { t.Fatal(err) } r := readGenerationLaunch(t, disk) if change != "unsettled" { if err := f.root.stop(t.Context(), f.peer, *f.req.Reservation); err != nil { t.Fatal(err) } } if change != "open-writers" { if err := f.journal.CloseWriters(t.Context(), f.target); err != nil { t.Fatal(err) } } prior := hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp} in, err := f.root.Guard.Root.Installation(t.Context(), f.target.Host, prior) if err != nil { t.Fatal(err) } next := in next.Stamp += 3 next.InventoryRevision++ f.claims.set(func(c *hostclaim.Claim) { c.Holder.Stamp = next.Stamp }) if err := f.root.Guard.RebindRootInventory(t.Context(), prior, in.InventoryRevision, next, []byte("root-verified-rebind"), func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil { t.Fatal(err) } switch change { case "wrong-installation": r.Binding.Installation = "other" case "wrong-incarnation": r.Binding.Target.Incarnation = "other" case "wrong-stamp": r.Binding.Target.Stamp++ case "wrong-account": r.Binding.Binding.Account = "other" case "wrong-enrollment": r.Binding.Target.Enrollment = "other" } calls := 0 verifier := generationRetirement{guard: f.root.Guard, journal: f.journal, kernel: func(context.Context, release.GenerationReservation) (string, error) { calls++ return r.Binding.Digest, nil }} _, err = verifier.verify(t.Context(), r) if change != "none" { if err == nil || calls != 0 { t.Fatalf("rebind erased old obligation: %v kernel=%d", err, calls) } return } if err != nil || calls != 1 { t.Fatalf("retained predecessor refused: %v kernel=%d", err, calls) } if err := verifier.retire(t.Context(), &release.RootGenerations{Store: disk}, r); err != nil { t.Fatal(err) } got := readGenerationLaunch(t, disk) if got.Retirement == "" { t.Fatal("exact old reservation retained after verified retirement") } current, err := f.root.Guard.Root.Installation(t.Context(), next.Host, hostguard.Captured{Incarnation: next.Incarnation, Stamp: next.Stamp}) if err != nil || current != next { t.Fatal("retirement changed successor installation", err) } }) } }