carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package host
4
5import (
6 "context"
7 "errors"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/hostactors"
11 "github.com/carlosframework/platform/internal/hostbox"
12)
13
14// N2 (roster binding, task 3b). A serving park's reboot preparation persists
15// the actor it was prepared under and its commit compares that record with a
16// fresh one. Root's inventory actor here is the production identity over a
17// signed roster, and every read of it follows another console create's hold
18// churn (a real Reserve and ReleaseFinishedHold), so the preparation's reread,
19// its persisted copy and the commit all see a different roster revision. They
20// still match: the preparation stores the bound actor. A quarantine and a
21// readmission between prepare and commit change the binding and are refused
22// (the quarantine by Verify, the readmission by the installation's stamp).
23func TestServingRebootPreparationSurvivesHoldChurn(t *testing.T) {
24 for _, step := range []string{"hold", "hold-root-restart", "quarantine-readmit"} {
25 t.Run(step, func(t *testing.T) {
26 ctx := t.Context()
27 roster := newBindingRosterFixture(t, "box-1", "ie")
28 identity := roster.identity()
29 actor, err := identity.Verify(ctx)
30 if err != nil {
31 t.Fatal(err)
32 }
33 if !hostactors.ValidBinding(actor.Binding) || actor.Binding != roster.binding() {
34 t.Fatalf("production actor binding %q", actor.Binding)
35 }
36 f, r := servingRebootPreparationFixtureFor(t, "parked", actor)
37 churning := func(identity hostbox.IdentityProvider) func(context.Context) (hostbox.Actor, error) {
38 return func(ctx context.Context) (hostbox.Actor, error) {
39 roster.holdChurn()
40 return identity.Verify(ctx)
41 }
42 }
43 f.p.root.InventoryActor = churning(identity)
44 id := r.Authorization.ID
45 before := roster.read().Roster.Revision
46 if err := f.p.prepareServingReboot(ctx, id); err != nil {
47 t.Fatal("prepare under hold churn:", err)
48 }
49 if after := roster.read().Roster.Revision; after < before+4 {
50 t.Fatalf("prepare read the actor without churn: revision %d -> %d", before, after)
51 }
52 body, err := f.disk.Read(servingRebootPreparationName(id, servingOtherBoot))
53 if err != nil {
54 t.Fatal(err)
55 }
56 var prep servingParkRebootPreparation
57 if servingParkDecode(body, &prep) != nil || prep.Active == nil || prep.Active.Actor != actor.Bound() || prep.Active.Actor.Revision != 0 {
58 t.Fatalf("persisted preparation actor: %+v", prep.Active)
59 }
60 roster.holdChurn()
61 p := f.p
62 switch step {
63 case "hold-root-restart":
64 // A restarted root: a new ledger and handler over the same
65 // private state, with a newly built identity.
66 ledger := *f.p.ledger
67 root := f.p.root
68 root.InventoryActor = churning(roster.identity())
69 p = &servingParkRoot{root: root, ledger: &ledger, empty: f.p.empty}
70 case "quarantine-readmit":
71 roster.quarantine("loss_1")
72 if err := p.commitServingReboot(ctx, id); err == nil {
73 t.Fatal("commit accepted while quarantined")
74 }
75 roster.readmit()
76 if b := roster.binding(); b == actor.Binding {
77 t.Fatal("readmission kept the binding")
78 }
79 }
80 err = p.commitServingReboot(ctx, id)
81 if step == "quarantine-readmit" {
82 // The installation's stamp is the old binding, so the fresh
83 // installed-state check (claimservingparksource_linux.go, the
84 // inventory against actor.Binding) refuses before the persisted
85 // preparation is compared: ErrTypedInventory, not ErrChanged.
86 if !errors.Is(err, hostbox.ErrTypedInventory) {
87 t.Fatalf("commit after readmission: %v, want ErrTypedInventory", err)
88 }
89 if _, err := f.disk.Read("current-namespace.json"); err == nil {
90 t.Fatal("refused commit bound the namespace")
91 }
92 return
93 }
94 if err != nil {
95 t.Fatal("commit after hold churn:", err)
96 }
97 fs := p.ledger.fs.(*servingParkFS)
98 if err := fs.recoverRecordedNamespace(ctx); err != nil {
99 t.Fatal(err)
100 }
101 current, err := p.ledger.read(id)
102 if err != nil || current.Source.BootID != servingOtherBoot {
103 t.Fatal("committed authority", err)
104 }
105 })
106 }
107}
108