carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "errors"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/activator"
10 "github.com/carlosframework/platform/internal/hostbox"
11 "github.com/carlosframework/platform/internal/hostclaim"
12 "github.com/carlosframework/platform/internal/hostguard"
13)
14
15// plan9Fix1StopAtCommitted admits a probe at probing through the real
16// runtime, then moves the stored op to probed and committed the way the moves
17// controller does, under KeepWriterSession and without touching the attempt.
18// The attempt's checkpoint still names probing.
19func plan9Fix1StopAtCommitted(t *testing.T) (*recoveryFixture, *recoveryController, *claimWakeAttempt, hostclaim.Observed) {
20 t.Helper()
21 probe := activator.StartPolicy{MaxStarts: 5, Window: 30 * time.Second, Probe: true}
22 f, c, _, _, admitted := plan9D2Landing(t, hostclaim.PhaseLandingProbing, probe, true)
23 a := c.wake.get(recoveryHost)
24 if a == nil {
25 t.Fatal("no pending attempt after admission")
26 }
27 for _, phase := range []string{hostclaim.PhaseLandingProbed, hostclaim.PhaseLandingCommitted} {
28 if _, err := hostclaim.Update(t.Context(), f.claims, f.read(), hostclaim.KeepWriterSession("m1", func(c *hostclaim.Claim) error {
29 c.Op.Phase = phase
30 return nil
31 })); err != nil {
32 t.Fatalf("move to %s: %v", phase, err)
33 }
34 }
35 if checkpoint, _ := a.snapshot(); checkpoint.Claim.Op.Phase != hostclaim.PhaseLandingProbing {
36 t.Fatalf("checkpoint moved to %q", checkpoint.Claim.Op.Phase)
37 }
38 return f, c, a, admitted
39}
40
41// R1: identifying a session in order to stop it grants no write authority.
42// A probe admitted at probing whose op the moves controller took to committed
43// is still stopped as itself by every teardown path: root fences and stops
44// that exact session at committed, the signed receipt verifies, and the
45// pending lease is released so the host's next admission is not refused.
46func TestPlan9Fix1TeardownStopsAProbeThatReachedCommitted(t *testing.T) {
47 t.Parallel()
48 for name, teardown := range map[string]func(context.Context, *recoveryController, hostclaim.Observed, hostclaim.Observed) error{
49 "stop": func(ctx context.Context, c *recoveryController, checkpoint, _ hostclaim.Observed) error {
50 return c.wake.Stop(ctx, checkpoint)
51 },
52 "close": func(ctx context.Context, c *recoveryController, _, _ hostclaim.Observed) error {
53 return c.wake.Close(ctx)
54 },
55 "failed-admission": func(ctx context.Context, c *recoveryController, _, admitted hostclaim.Observed) error {
56 return c.runtime.FailedAdmission(ctx, admitted, errors.New("restore failed"))
57 },
58 } {
59 t.Run(name, func(t *testing.T) {
60 f, c, a, admitted := plan9Fix1StopAtCommitted(t)
61 ctx := t.Context()
62 checkpoint, _ := a.snapshot()
63 if err := teardown(ctx, c, checkpoint, admitted); err != nil {
64 t.Fatalf("teardown refused the probe's own session at committed: %v", err)
65 }
66 h := admitted.Claim.Holder
67 if len(f.requests) != 1 {
68 t.Fatalf("root requests = %d, want 1", len(f.requests))
69 }
70 req := f.requests[0]
71 if req.Recover || req.Phase != hostclaim.PhaseLandingCommitted || req.OperationID != "m1" || req.WriterSession != h.Session || req.Incarnation != h.Incarnation || req.InstallationStamp != h.Stamp || req.ObligationID != hostguard.WriterStopObligation(h.Session) || req.Box != "box-1" || req.ControllerID != c.local.ControllerID {
72 t.Fatalf("root was asked to stop another tuple: %+v", req)
73 }
74 receipts, err := f.guard.CompletedWriterStops(ctx, recoveryHost, hostguard.Captured{Incarnation: h.Incarnation, Stamp: h.Stamp}, "m1", "box-1")
75 if err != nil || len(receipts) != 1 || receipts[0].Target.Session != h.Session || receipts[0].Target.Phase != hostclaim.PhaseLandingCommitted || verifyRetirementReceipt(ctx, receipts[0]) != nil {
76 t.Fatalf("no verified writer-stop receipt for session %d: %+v %v", h.Session, receipts, err)
77 }
78 if c.wake.get(recoveryHost) != nil || !a.released || a.ctx.Err() == nil {
79 t.Fatalf("pending lease kept: pending=%v released=%v", c.wake.get(recoveryHost) != nil, a.released)
80 }
81 })
82 }
83}
84
85// A teardown never follows a session backward: a checkpoint at committed
86// does not stop a claim now back at probing, and root is never asked.
87func TestPlan9Fix1TeardownRefusesABackwardPhase(t *testing.T) {
88 f, c, a, _ := plan9Fix1StopAtCommitted(t)
89 ctx := t.Context()
90 committed := f.read()
91 a.mu.Lock()
92 a.observed = committed
93 a.mu.Unlock()
94 if _, err := hostclaim.Update(ctx, f.claims, committed, func(c *hostclaim.Claim) error {
95 c.Op.Phase = hostclaim.PhaseLandingProbing
96 return nil
97 }); err != nil {
98 t.Fatal(err)
99 }
100 if err := c.wake.Stop(ctx, committed); !errors.Is(err, hostbox.ErrChanged) || len(f.requests) != 0 {
101 t.Fatalf("stop followed the session backward: %v requests=%d", err, len(f.requests))
102 }
103 if err := c.wake.Close(ctx); !errors.Is(err, hostbox.ErrChanged) || len(f.requests) != 0 || c.wake.get(recoveryHost) == nil {
104 t.Fatalf("close followed the session backward: %v requests=%d pending=%v", err, len(f.requests), c.wake.get(recoveryHost) != nil)
105 }
106}
107
108func TestPlan9Fix1StopReleasesCheckpointThatAdvancedAfterCapture(t *testing.T) {
109 f, c, a, _ := plan9Fix1StopAtCommitted(t)
110 captured, _ := a.snapshot()
111 // Another successful check advances the pending attempt after teardown
112 // captured its identity. Root still verifies and stops the same session.
113 a.mu.Lock()
114 a.observed = f.read()
115 a.mu.Unlock()
116 if err := c.wake.Stop(t.Context(), captured); err != nil {
117 t.Fatal(err)
118 }
119 if len(f.requests) != 1 || c.wake.get(recoveryHost) != nil || !a.released {
120 t.Fatalf("stopped session kept an advanced checkpoint: requests=%d pending=%v released=%v", len(f.requests), c.wake.get(recoveryHost) != nil, a.released)
121 }
122}
123