carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "testing"
9 "time"
10
11 "github.com/carlosframework/platform/internal/release"
12)
13
14func retryAttempt(id string, session uint64) WakeRetryAttempt {
15 return WakeRetryAttempt{ID: id, Box: "adi-knitting", Enrollment: "enroll-1", BootID: "boot-1",
16 ControllerID: "controller-1", Session: session, InvocationID: "invoke-" + id, LaunchInputs: "sha256-inputs-" + id}
17}
18
19func TestWakeRetryDurableBudgetAndBackoff(t *testing.T) {
20 ctx := context.Background()
21 s := release.NewDirStore(t.TempDir())
22 o := servingClaim(t, ctx, s)
23 inc := o.Claim.Holder.Incarnation
24 now := time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC)
25
26 for n := 1; n <= WakeRetryLimit; n++ {
27 a := retryAttempt(string(rune('0'+n)), o.Claim.Holder.Session)
28 var err error
29 o, err = Update(ctx, s, o, ReserveWakeRetryAttempt(WakeRetryReservation{
30 HolderIncarnation: inc, EpisodeID: "episode-1", Attempt: a, Now: now,
31 }))
32 if err != nil {
33 t.Fatalf("reserve %d: %v", n, err)
34 }
35 if o.Claim.Holder.WakeRetry.AttemptsReserved != n || o.Claim.Holder.WakeRetry.State != "attempting" {
36 t.Fatalf("reserved %d = %+v", n, o.Claim.Holder.WakeRetry)
37 }
38 // A reread proves the budget is stored in the claim rather than process memory.
39 if o, err = Read(ctx, s, testHost); err != nil {
40 t.Fatal(err)
41 }
42 o, err = Update(ctx, s, o, FailWakeRetryAttempt(WakeRetryResult{
43 HolderIncarnation: inc, EpisodeID: "episode-1", Attempt: a, Now: now,
44 }))
45 if err != nil {
46 t.Fatalf("fail %d: %v", n, err)
47 }
48 wantState := "open"
49 if n == WakeRetryLimit {
50 wantState = "failed"
51 }
52 if r := o.Claim.Holder.WakeRetry; r.State != wantState || !r.NotBefore.Equal(now.Add(wakeRetryDelays[n-1])) {
53 t.Fatalf("settled %d = %+v", n, r)
54 }
55 if _, err := Update(ctx, s, o, ReserveWakeRetryAttempt(WakeRetryReservation{HolderIncarnation: inc, Attempt: retryAttempt("early", 0), Now: now})); !errors.Is(err, ErrWrongState) {
56 t.Fatalf("early reserve %d = %v", n, err)
57 }
58 now = now.Add(wakeRetryDelays[n-1])
59 }
60 if _, err := Update(ctx, s, o, ReserveWakeRetryAttempt(WakeRetryReservation{HolderIncarnation: inc, Attempt: retryAttempt("six", 0), Now: now})); !errors.Is(err, ErrWrongState) {
61 t.Fatalf("sixth reserve = %v", err)
62 }
63 if _, err := Update(ctx, s, o, AdmitWriter(Admission{Region: "ie", PredecessorRegion: "ie", Box: "adi-knitting", Mode: "cold", NewGeneration: "new-after-failure", PredecessorCertified: true})); !errors.Is(err, ErrWrongState) {
64 t.Fatalf("serving admission after exhausted budget = %v", err)
65 }
66}
67
68func TestWakeRetryColdAdmissionBindingAndExactHealth(t *testing.T) {
69 ctx := context.Background()
70 s := release.NewDirStore(t.TempDir())
71 o := servingClaim(t, ctx, s)
72 inc := o.Claim.Holder.Incarnation
73 a := retryAttempt("first", 0)
74 now := time.Date(2026, 10, 6, 13, 0, 0, 0, time.UTC)
75 var err error
76 o, err = Update(ctx, s, o, ReserveWakeRetryAttempt(WakeRetryReservation{HolderIncarnation: inc, EpisodeID: "episode", Attempt: a, Now: now}))
77 if err != nil {
78 t.Fatal(err)
79 }
80 o, err = Update(ctx, s, o, AdmitWriter(Admission{Region: "ie", PredecessorRegion: "ie", Box: "adi-knitting", Mode: "cold", NewGeneration: "generation-1", PredecessorCertified: true}))
81 if err != nil {
82 t.Fatal(err)
83 }
84 session := o.Claim.Holder.Session
85 if o.Claim.Holder.WakeRetry.AttemptsReserved != 1 { // cold admission did not reset the episode
86 t.Fatalf("episode after admission = %+v", o.Claim.Holder.WakeRetry)
87 }
88 if _, err := Update(ctx, s, o, ClearWakeRetryHealthy(WakeRetryHealth{WakeRetryResult: WakeRetryResult{HolderIncarnation: inc, EpisodeID: "episode", Attempt: a}, ReadinessPassed: true})); !errors.Is(err, ErrWrongState) {
89 t.Fatalf("health before session binding = %v", err)
90 }
91 o, err = Update(ctx, s, o, BindWakeRetrySession(inc, "episode", "first", session))
92 if err != nil {
93 t.Fatal(err)
94 }
95 a.Session = session
96 wrong := a
97 wrong.InvocationID = "other-invocation"
98 if _, err := Update(ctx, s, o, ClearWakeRetryHealthy(WakeRetryHealth{WakeRetryResult: WakeRetryResult{HolderIncarnation: inc, EpisodeID: "episode", Attempt: wrong}, ReadinessPassed: true})); !errors.Is(err, ErrWrongState) {
99 t.Fatalf("wrong invocation health = %v", err)
100 }
101 if _, err := Update(ctx, s, o, ClearWakeRetryHealthy(WakeRetryHealth{WakeRetryResult: WakeRetryResult{HolderIncarnation: inc, EpisodeID: "episode", Attempt: a}})); !errors.Is(err, ErrWrongState) {
102 t.Fatalf("failed readiness = %v", err)
103 }
104 o, err = Update(ctx, s, o, ClearWakeRetryHealthy(WakeRetryHealth{WakeRetryResult: WakeRetryResult{HolderIncarnation: inc, EpisodeID: "episode", Attempt: a}, ReadinessPassed: true}))
105 if err != nil || o.Claim.Holder.WakeRetry != nil {
106 t.Fatalf("healthy clear = %+v, %v", o.Claim.Holder.WakeRetry, err)
107 }
108}
109
110func TestWakeRetryOperatorResetRequiresSettledAttempt(t *testing.T) {
111 ctx := context.Background()
112 s := release.NewDirStore(t.TempDir())
113 o := servingClaim(t, ctx, s)
114 inc := o.Claim.Holder.Incarnation
115 a := retryAttempt("active", o.Claim.Holder.Session)
116 var err error
117 o, err = Update(ctx, s, o, ReserveWakeRetryAttempt(WakeRetryReservation{HolderIncarnation: inc, EpisodeID: "old", Attempt: a, Now: time.Now()}))
118 if err != nil {
119 t.Fatal(err)
120 }
121 reset := WakeRetryReset{HolderIncarnation: inc, ObservedEpisode: "old", NewEpisodeID: "new", OperatorRequest: "request-42"}
122 if _, err := Update(ctx, s, o, ResetWakeRetry(reset)); !errors.Is(err, ErrWrongState) {
123 t.Fatalf("unsettled reset = %v", err)
124 }
125 reset.AttemptSettled = true
126 o, err = Update(ctx, s, o, ResetWakeRetry(reset))
127 if err != nil {
128 t.Fatal(err)
129 }
130 if got := o.Claim.Holder.WakeRetry; got.EpisodeID != "new" || got.State != "open" || got.AttemptsReserved != 0 || got.ResetRequest != "request-42" {
131 t.Fatalf("reset episode = %+v", got)
132 }
133}
134
135func TestWakeRetryValidationAndWireShape(t *testing.T) {
136 ctx := context.Background()
137 s := release.NewDirStore(t.TempDir())
138 o := servingClaim(t, ctx, s)
139 bad := []WakeRetry{
140 {EpisodeID: "e", State: "open", AttemptsReserved: 5},
141 {EpisodeID: "e", State: "attempting", AttemptsReserved: 1},
142 {EpisodeID: "e", State: "failed", AttemptsReserved: 4, NotBefore: time.Now()},
143 {EpisodeID: "e", State: "unknown"},
144 }
145 for i := range bad {
146 if _, err := Update(ctx, s, o, func(c *Claim) error { r := bad[i]; c.Holder.WakeRetry = &r; return nil }); !errors.Is(err, ErrMalformed) {
147 t.Errorf("bad %d = %v", i, err)
148 }
149 }
150 raw, err := json.Marshal(o.Claim.Holder)
151 if err != nil {
152 t.Fatal(err)
153 }
154 if bytes.Contains(raw, []byte(`"wake_retry"`)) {
155 t.Fatalf("legacy holder gained wake_retry: %s", raw)
156 }
157}
158