carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostrelocate
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "testing"
11 "time"
12
13 "github.com/carlosframework/platform/internal/hostclaim"
14 "github.com/carlosframework/platform/internal/hostfiles"
15 "github.com/carlosframework/platform/internal/instancekey"
16 "github.com/carlosframework/platform/internal/release"
17)
18
19// O2' (instance-key plan rev 3, replacing stage B's O2 and ruling 55's
20// KeyEvidence reader): a nonempty database's pinned manifest evidences its
21// key by (a) instance.key, or (b') an adoption record bound to the pinned
22// writer for the key the target will deliver. Presence of a key, in the env
23// or in the configuration, is never evidence (Astra rounds 1 and 2).
24func TestStorePinnerKeyEvidence(t *testing.T) {
25 ctx := context.Background()
26 at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC)
27 const key = "q+/Zk3vJ0aW1Yx9sR7tUe2oPl4mN8bC6dF5gH0iJ1kA="
28 writer := instancekey.Binding{Host: host, Incarnation: "inc-1", Session: 3, Generation: "g3"}
29 earlier := instancekey.Binding{Host: host, Incarnation: "inc-1", Session: 2, Generation: "g2"}
30 envWithKey := "CARLOS_ADMIN_TOKEN=t\nCARLOS_INSTANCE_KEY=" + key + "\n"
31 rec := func(b instancekey.Binding, source, k string) string {
32 return string(instancekey.AdoptionRecord(b, source, instancekey.Digest(k)))
33 }
34 for _, tc := range []struct {
35 name string
36 appKey bool // (a) instance.key in the manifest
37 env, adoption string // "" is no item
38 unreadable string // a named item whose object is missing
39 holder func(*hostclaim.Holder)
40 want error
41 pinned *KeyPin
42 }{
43 {name: "(a) the app's own instance.key", appKey: true, env: envWithKey, want: nil},
44 {name: "(b') the platform key, adopted by the pinned writer", env: envWithKey, adoption: rec(writer, "platform", key), pinned: &KeyPin{Source: "platform", SHA256: instancekey.Digest(key)}},
45 {name: "(b') a configured key, adopted by the pinned writer", env: "CARLOS_ADMIN_TOKEN=t\n", adoption: rec(writer, "config", "configured-A"), pinned: &KeyPin{Source: "config", SHA256: instancekey.Digest("configured-A")}},
46 {name: "(b') a configured key under an empty override", env: "CARLOS_ADMIN_TOKEN=t\nCARLOS_INSTANCE_KEY=\n", adoption: rec(writer, "config", "configured-A"), pinned: &KeyPin{Source: "config", SHA256: instancekey.Digest("configured-A")}},
47 {name: "a backed-up platform key, the app's own key missing", env: envWithKey, want: ErrFilesUnevidenced},
48 {name: "configured A, the app's own unpublished B: no report, no record", env: "CARLOS_ADMIN_TOKEN=t\n", want: ErrFilesUnevidenced},
49 {name: "a stale record from a previous release", env: envWithKey, adoption: rec(earlier, "platform", key), want: ErrFilesUnevidenced},
50 {name: "a rollback: a new session, the old session's record", env: envWithKey, adoption: rec(writer, "platform", key),
51 holder: func(h *hostclaim.Holder) {
52 h.Session, h.Generation = 4, "g4"
53 h.Admissions = append(h.Admissions, hostclaim.AdmissionRecord{Generation: "g4", Predecessor: "g3", Session: 4})
54 }, want: ErrFilesUnevidenced},
55 {name: "a record from another installation", env: envWithKey, adoption: rec(instancekey.Binding{Host: host, Incarnation: "inc-0", Session: 3, Generation: "g3"}, "platform", key), want: ErrFilesUnevidenced},
56 {name: "a platform record for another key", env: envWithKey, adoption: rec(writer, "platform", "another-key"), want: ErrFilesUnevidenced},
57 {name: "a config record while the env's own key overrides it", env: envWithKey, adoption: rec(writer, "config", key), want: ErrFilesUnevidenced},
58 {name: "a platform record without the platform key", env: "CARLOS_ADMIN_TOKEN=t\n", adoption: rec(writer, "platform", key), want: ErrFilesUnevidenced},
59 {name: "an exported key line is not what the app receives", env: "export CARLOS_INSTANCE_KEY=" + key + "\n", adoption: rec(writer, "platform", key), want: ErrFilesUnevidenced},
60 {name: "a malformed record", env: envWithKey, adoption: `{"v":1}`, want: ErrFilesUnevidenced},
61 {name: "no admitted writer for the pinned generation", env: envWithKey, adoption: rec(writer, "platform", key), holder: func(h *hostclaim.Holder) { h.Incarnation = "" }, want: ErrFilesUnevidenced},
62 {name: "a named adoption item that cannot be read", env: envWithKey, unreadable: instancekey.AdoptionItem, want: errUnreadable},
63 {name: "a named env item that cannot be read", adoption: rec(writer, "config", "configured-A"), unreadable: hostfiles.EnvItem, want: errUnreadable},
64 } {
65 t.Run(tc.name, func(t *testing.T) {
66 claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir())
67 obs := pinClaim()
68 obs.Claim.Holder.Incarnation = "inc-1"
69 gen := "g3"
70 if tc.holder != nil {
71 tc.holder(obs.Claim.Holder)
72 gen = obs.Claim.Holder.Generation
73 }
74 m := withItem(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 4, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}}, hostfiles.WakeItem, "d")
75 if tc.appKey {
76 m = keyed(m)
77 }
78 item := func(name, body string, put bool) {
79 sum := sha256.Sum256([]byte(body))
80 sha := hex.EncodeToString(sum[:])
81 obj := pinLocation + "/files/g-" + gen + "/objects/" + fmt.Sprintf("%016d-%s", 4, sha)
82 if put {
83 if _, err := data.Put(ctx, obj, []byte(body), ""); err != nil {
84 t.Fatal(err)
85 }
86 }
87 m.Platform[name] = hostfiles.Entry{Object: obj, SHA256: sha, Size: int64(len(body)), Mode: 0o600, Seq: 4}
88 }
89 if tc.env != "" {
90 item(hostfiles.EnvItem, tc.env, true)
91 }
92 if tc.adoption != "" {
93 item(instancekey.AdoptionItem, tc.adoption, true)
94 }
95 if tc.unreadable != "" {
96 item(tc.unreadable, "never stored\n", false)
97 }
98 putManifest(t, data, gen, m)
99 p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)}
100 ref, err := p.Pin(ctx, obs, at)
101 switch {
102 case tc.want == errUnreadable:
103 if err == nil || errors.Is(err, ErrFilesUnevidenced) {
104 t.Fatalf("got %v, want a read error, never a refusal for no evidence", err)
105 }
106 return
107 case !errors.Is(err, tc.want):
108 t.Fatalf("got %v, want %v", err, tc.want)
109 }
110 if err != nil {
111 return
112 }
113 raw, _, err := claims.Get(ctx, ref.Key)
114 if err != nil {
115 t.Fatal(err)
116 }
117 var obj PinObject
118 if err := json.Unmarshal(raw, &obj); err != nil {
119 t.Fatal(err)
120 }
121 if (obj.InstanceKey == nil) != (tc.pinned == nil) || (tc.pinned != nil && *obj.InstanceKey != *tc.pinned) {
122 t.Fatalf("pinned key %+v, want %+v", obj.InstanceKey, tc.pinned)
123 }
124 })
125 }
126}
127
128var errUnreadable = errors.New("sentinel: any read error")
129
130// A configured key's record vouches only beside the env item that shows
131// the instance's own file supplies no key. A manifest without host.env is
132// not evidence that nothing overrides the configuration at launch (absence
133// is never evidence): the pin refuses, and the same record beside an env
134// item with no key of its own pins.
135func TestStorePinnerConfigRecordNeedsTheEnvItem(t *testing.T) {
136 ctx := context.Background()
137 at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC)
138 writer := instancekey.Binding{Host: host, Incarnation: "inc-1", Session: 3, Generation: "g3"}
139 record := string(instancekey.AdoptionRecord(writer, instancekey.SourceConfig, instancekey.Digest("configured-A")))
140 for _, tc := range []struct {
141 name string
142 env string // "" or "item"
143 want error
144 }{
145 {"an env item never published", "", ErrFilesUnevidenced},
146 {"an env item with no key of its own", "item", nil},
147 } {
148 t.Run(tc.name, func(t *testing.T) {
149 claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir())
150 obs := pinClaim()
151 obs.Claim.Holder.Incarnation = "inc-1"
152 m := withItem(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 4, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}}, hostfiles.WakeItem, "d")
153 item := func(name, body string) {
154 sum := sha256.Sum256([]byte(body))
155 sha := hex.EncodeToString(sum[:])
156 obj := pinLocation + "/files/g-g3/objects/" + fmt.Sprintf("%016d-%s", 4, sha)
157 if _, err := data.Put(ctx, obj, []byte(body), ""); err != nil {
158 t.Fatal(err)
159 }
160 m.Platform[name] = hostfiles.Entry{Object: obj, SHA256: sha, Size: int64(len(body)), Mode: 0o600, Seq: 4}
161 }
162 item(instancekey.AdoptionItem, record)
163 if tc.env == "item" {
164 item(hostfiles.EnvItem, "CARLOS_ADMIN_TOKEN=t\n")
165 }
166 putManifest(t, data, "g3", m)
167 p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)}
168 ref, err := p.Pin(ctx, obs, at)
169 if tc.want != nil {
170 if !errors.Is(err, tc.want) {
171 t.Fatalf("got %v, want %v", err, tc.want)
172 }
173 return
174 }
175 if err != nil {
176 t.Fatal(err)
177 }
178 obj, err := ReadPin(ctx, claims, ref)
179 if err != nil {
180 t.Fatal(err)
181 }
182 want := KeyPin{Source: instancekey.SourceConfig, SHA256: instancekey.Digest("configured-A")}
183 if obj.InstanceKey == nil || *obj.InstanceKey != want {
184 t.Fatalf("pinned key %+v, want %+v", obj.InstanceKey, want)
185 }
186 })
187 }
188}
189