carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package park
2
3import (
4 "context"
5 "errors"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostclaim"
10 "github.com/carlosframework/platform/internal/moves"
11)
12
13// TestParkScopeSurvivesRestart is witness wParkScopeSurvivesRestart (claim
14// rev 6.23 §2, INV-22, Plan 5 ruling K8). A moves chore is queued with its
15// scope bound in the queuing CAS. After a restart the caller computes its
16// bound again, and the new one is later; neither a second queue nor a
17// park.Engine.Bind with it moves the scope: both are refused, the claim is
18// not written, and the first scope stands. A fresh engine then takes its
19// deadline from the persisted scope: pending before it, acknowledged after.
20func TestParkScopeSurvivesRestart(t *testing.T) {
21 f := newFixture(t)
22 ctx := t.Context()
23 obs, err := hostclaim.Read(ctx, f.e.Claims, f.w.Host)
24 if err != nil {
25 t.Fatal(err)
26 }
27 inc := obs.Claim.Holder.Incarnation
28 sealBound := f.ob.Park.EvidenceAt
29 first := moves.StagingScope(f.w.Host, f.w.Op, inc, sealBound)
30 obs, err = hostclaim.Update(ctx, f.e.Claims, obs, func(c *hostclaim.Claim) error {
31 c.Obligations = nil
32 return hostclaim.QueueStagingPark(f.w.Op, "ie", first, f.e.NewID)(c)
33 })
34 if err != nil {
35 t.Fatalf("queue the lane park: %v", err)
36 }
37 if len(obs.Claim.Obligations) != 1 {
38 t.Fatalf("obligations = %+v, want the lane park alone", obs.Claim.Obligations)
39 }
40 lane := obs.Claim.Obligations[0]
41 w := Work{Host: f.w.Host, ID: lane.ID, Op: lane.Op}
42
43 // The restart: the bound is computed again, a minute later.
44 later := moves.StagingScope(f.w.Host, f.w.Op, inc, sealBound.Add(time.Minute))
45 stands := func(when string) {
46 t.Helper()
47 now, err := hostclaim.Read(ctx, f.e.Claims, f.w.Host)
48 if err != nil {
49 t.Fatal(err)
50 }
51 if now.ETag != obs.ETag {
52 t.Fatalf("%s: the claim was written", when)
53 }
54 if len(now.Claim.Obligations) != 1 || now.Claim.Obligations[0].Park == nil || !now.Claim.Obligations[0].Park.EvidenceAt.Equal(sealBound) {
55 t.Fatalf("%s: the first scope does not stand: %+v", when, now.Claim.Obligations)
56 }
57 }
58 if _, err := hostclaim.Update(ctx, f.e.Claims, obs, hostclaim.QueueStagingPark(f.w.Op, "ie", later, f.e.NewID)); !errors.Is(err, hostclaim.ErrWrongState) {
59 t.Fatalf("a second queue with a later bound: %v, want ErrWrongState", err)
60 }
61 stands("after the second queue")
62 if err := f.e.Bind(ctx, w, later); !errors.Is(err, ErrChanged) {
63 t.Fatalf("a bind with a later bound: %v, want ErrChanged", err)
64 }
65 stands("after the bind")
66 if err := f.e.Bind(ctx, w, first); err != nil {
67 t.Fatalf("a bind with the persisted scope: %v", err)
68 }
69 stands("after the same bind")
70
71 // A fresh engine (the agent restarted) reads its deadline from the claim.
72 restarted := f.e
73 restarted.View = func(_ context.Context, o hostclaim.Observed) (View, error) {
74 return View{ClaimETag: o.ETag, Region: "ie", AuthoritativeRegion: "ie", MarkerKnown: true, MarkerGeneration: "unrelated", GenerationOf: func(Ref) (string, bool) { return "live", true }}, nil
75 }
76 f.b.Put("data", first.Location+"/replica/0000/data", []byte("staged"))
77 deadline := sealBound.Add(SignatureAge + first.RequestLimit)
78 f.now = deadline.Add(-time.Second)
79 restarted.After = func(step string) error {
80 if step == "verify" {
81 f.now = deadline.Add(time.Hour)
82 }
83 return nil
84 }
85 if err := restarted.Run(ctx, w); !errors.Is(err, ErrPending) {
86 t.Fatalf("a run whose first listing is before the persisted deadline: %v, want ErrPending", err)
87 }
88 after, err := hostclaim.Read(ctx, f.e.Claims, f.w.Host)
89 if err != nil {
90 t.Fatal(err)
91 }
92 if len(after.Claim.Obligations) != 1 || !after.Claim.Obligations[0].Park.EvidenceAt.Equal(sealBound) {
93 t.Fatalf("the run moved the scope: %+v", after.Claim.Obligations)
94 }
95 restarted.After = nil
96 if err := restarted.Run(ctx, w); err != nil {
97 t.Fatalf("a run after the persisted deadline: %v", err)
98 }
99 after, err = hostclaim.Read(ctx, f.e.Claims, f.w.Host)
100 if err != nil {
101 t.Fatal(err)
102 }
103 if len(after.Claim.Obligations) != 0 || !after.Claim.QuietAfter["ie"].Equal(deadline) {
104 t.Fatalf("after the deadline: obligations %+v, quiet_after %v, want none and %v", after.Claim.Obligations, after.Claim.QuietAfter["ie"], deadline)
105 }
106}
107