carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostops
2
3import (
4 "encoding/json"
5 "errors"
6
7 "github.com/carlosframework/platform/internal/hostclaim"
8 "github.com/carlosframework/platform/internal/release"
9 "testing"
10)
11
12func TestWakeRetryResetFinishRevokesGrantAndCannotFinishOtherKinds(t *testing.T) {
13 for _, kind := range []string{RegistrationWakeRetryReset, "create"} {
14 t.Run(kind, func(t *testing.T) {
15 w, raw, clock := testWriter(t)
16 seedAdmission(t, raw, "bab", "demo", openAdmission("app"))
17 h, err := w.RegisterStart(t.Context(), "bab", "demo", "app", "reset", Registration{Kind: kind, Host: "x.example", Owner: testOwner, AppID: "app", Epoch: 1, Claim: SavedClaim{Absent: true}}, clock())
18 if err != nil {
19 t.Fatal(err)
20 }
21 err = h.FinishWakeRetryReset(t.Context())
22 if kind != RegistrationWakeRetryReset {
23 if !errors.Is(err, ErrPending) {
24 t.Fatalf("finished %s with reset handle: %v", kind, err)
25 }
26 return
27 }
28 if err != nil {
29 t.Fatal(err)
30 }
31 grants := h.Lifecycle(t.Context()).Value(lifecycleGrantKey{}).([]lifecycleGrant)
32 if live, err := grantLive(t.Context(), raw, grants[0]); err != nil || live {
33 t.Fatalf("finished grant still live: %v %v", live, err)
34 }
35 if err := h.Renew(t.Context(), clock()); !errors.Is(err, ErrPending) {
36 t.Fatalf("finished registration renewed: %v", err)
37 }
38 if err := w.SettleRegistration(t.Context(), "bab", "demo", "app", h.ID(), clock()); err != nil {
39 t.Fatal(err)
40 }
41 })
42 }
43}
44
45func TestPurgeDrainsFailedWakeResetWithoutChangingServingClaim(t *testing.T) {
46 w, raw, clock := testWriter(t)
47 ctx := t.Context()
48 seedAdmission(t, raw, "bab", "demo", openAdmission("app"))
49 host := "x.example"
50 nh := hostclaim.NewHolder{Family: "instance", Key: "apps/bab/demo/instances/x.example.json", Account: "bab", App: "demo", Host: host, Region: "ie", RecordRegion: "ie", CreatedAt: clock()}
51 record := []byte(`{"schema":1,"host":"x.example","account":"bab","app":"demo","region":"ie","created_at":"2026-10-07T12:00:00Z"}`)
52 o, err := hostclaim.Read(ctx, raw, host)
53 if err != nil {
54 t.Fatal(err)
55 }
56 o, err = hostclaim.Update(ctx, raw, o, hostclaim.BeginCreate(nh, record, "create", "console", clock()))
57 if err != nil {
58 t.Fatal(err)
59 }
60 o, err = hostclaim.Update(ctx, raw, o, hostclaim.CompleteCreate("create", "box"))
61 if err != nil {
62 t.Fatal(err)
63 }
64 h, err := w.RegisterStart(ctx, "bab", "demo", "app", "reset", Registration{Kind: RegistrationWakeRetryReset, Host: host, Owner: testOwner, AppID: "app", Epoch: 1, Claim: SavedObservation(o), Incarnation: o.Claim.Holder.Incarnation, CreatedAt: clock()}, clock())
65 if err != nil {
66 t.Fatal(err)
67 }
68 tombstone := release.AppTombstoneKey("bab", "demo")
69 tag, err := raw.Put(ctx, tombstone, []byte(`{}`), "")
70 if err != nil {
71 t.Fatal(err)
72 }
73 j := PurgeJournal{V: 1, Purge: Purge{ID: "purge", Phase: "reserved"}, AppID: "app", Account: "bab", App: "demo", Hosts: map[string]PurgeHost{}, Tombstone: PurgeObject{Key: tombstone, ETag: tag, Body: json.RawMessage(`{}`)}}
74 if _, err := w.ReservePurge(ctx, j, testOwner, clock()); err != nil {
75 t.Fatal(err)
76 }
77 if _, err := w.BeginPurge(ctx, "bab", "demo", "app", "purge", testOwner); err != nil {
78 t.Fatal(err)
79 }
80 if err := w.DrainPurgeRegistrations(ctx, "bab", "demo", "app", "purge", clock(), nil); !errors.Is(err, ErrPending) {
81 t.Fatalf("live reset drained by purge: %v", err)
82 }
83 if err := h.FinishWakeRetryReset(ctx); err != nil {
84 t.Fatal(err)
85 }
86 if err := w.DrainPurgeRegistrations(ctx, "bab", "demo", "app", "purge", clock(), nil); err != nil {
87 t.Fatalf("failed reset blocked purge: %v", err)
88 }
89 fresh, err := hostclaim.Read(ctx, raw, host)
90 if err != nil || fresh.ETag != o.ETag {
91 t.Fatalf("draining reset mutated serving claim: %v", err)
92 }
93 j, _, err = ReadPurgeJournal(ctx, raw, "bab", "demo", "purge", "app")
94 if err != nil || len(j.Hosts[host].Registrations["reset"]) == 0 || j.Hosts[host].FenceState != "" {
95 t.Fatalf("registration was not journaled without a claim fence: %+v %v", j.Hosts[host], err)
96 }
97 adm, err := ReadAdmission(ctx, raw, "bab", "demo", "app")
98 if err != nil || len(adm.Value.Registrations) != 0 {
99 t.Fatalf("drained registration remains: %+v %v", adm.Value.Registrations, err)
100 }
101}
102