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 "encoding/base64"
6 "errors"
7 "io"
8 "net/http"
9 "strings"
10 "testing"
11 "time"
12
13 "github.com/carlosframework/platform/internal/boxid"
14 "github.com/carlosframework/platform/internal/release"
15)
16
17// fakeIMDS answers IMDSv2 in-process: no socket, so it runs in any sandbox.
18type fakeIMDS struct {
19 doc, sig string
20 status int // for the document and signature; 0 = 200
21 calls []string
22}
23
24func (f *fakeIMDS) RoundTrip(r *http.Request) (*http.Response, error) {
25 f.calls = append(f.calls, r.Method+" "+r.URL.Path)
26 answer := func(code int, body string) (*http.Response, error) {
27 return &http.Response{StatusCode: code, Body: io.NopCloser(strings.NewReader(body)), Header: http.Header{}, Request: r}, nil
28 }
29 if r.Method == http.MethodPut && r.URL.Path == "/latest/api/token" {
30 return answer(200, "tok\n")
31 }
32 if r.Header.Get("X-aws-ec2-metadata-token") != "tok" {
33 return answer(401, "")
34 }
35 code := f.status
36 if code == 0 {
37 code = 200
38 }
39 switch r.URL.Path {
40 case imdsIdentityDocument:
41 return answer(code, f.doc)
42 case imdsIdentitySignature:
43 return answer(code, f.sig)
44 }
45 return answer(404, "")
46}
47
48func imdsClient(f *fakeIMDS) *http.Client { return &http.Client{Transport: f} }
49
50var recordNow = time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC)
51
52// The document is signed byte for byte, so its trailing newline must
53// survive; the signature arrives wrapped and must not.
54func TestReadIdentityRecordKeepsTheDocumentsExactBytes(t *testing.T) {
55 doc := "{\n \"accountId\" : \"111122223333\",\n \"instanceId\" : \"i-0abc\"\n}\n"
56 sig := base64.StdEncoding.EncodeToString([]byte(strings.Repeat("s", 256)))
57 wrapped := sig[:64] + "\n" + sig[64:] + "\n"
58 f := &fakeIMDS{doc: doc, sig: wrapped}
59 rec, err := readIdentityRecord(imdsClient(f), "http://imds.test", "annie-welding", recordNow)
60 if err != nil {
61 t.Fatal(err)
62 }
63 got, _ := base64.StdEncoding.DecodeString(rec.IdentityDocument)
64 if string(got) != doc {
65 t.Fatalf("document = %q, want the bytes IMDS served, %q", got, doc)
66 }
67 if rec.IdentitySignature != sig || rec.V != 1 || rec.Box != "annie-welding" || rec.WrittenAt != "2026-10-06T09:00:00Z" {
68 t.Fatalf("record = %+v", rec)
69 }
70}
71
72func TestReadIdentityRecordRefusesWhatIsNotAnAnswer(t *testing.T) {
73 for name, f := range map[string]*fakeIMDS{
74 "document missing": {doc: "{}", sig: "c2lnCg==", status: 404},
75 "signature not base64": {doc: "{}", sig: "not base64!"},
76 "signature empty": {doc: "{}", sig: "\n"},
77 } {
78 t.Run(name, func(t *testing.T) {
79 if rec, err := readIdentityRecord(imdsClient(f), "http://imds.test", "annie-welding", recordNow); err == nil {
80 t.Fatalf("record = %+v, want an error", rec)
81 }
82 })
83 }
84}
85
86// identityRecord is a record whose document names instance; the
87// signature is not checked on this path.
88func identityRecord(instance, writtenAt string) boxid.Record {
89 doc := base64.StdEncoding.EncodeToString([]byte(`{"accountId":"111122223333","region":"eu-west-1","instanceId":"` + instance + `"}`))
90 return boxid.Record{V: 1, Box: "annie-welding", IdentityDocument: doc, IdentitySignature: "c2ln", WrittenAt: writtenAt}
91}
92
93func TestPublishBoxRecordWritesOnceAndNeverRewrites(t *testing.T) {
94 ctx, st := context.Background(), release.NewDirStore(t.TempDir())
95 writtenAt := "2026-10-06T09:00:00Z"
96 read := func() (boxid.Record, error) { return identityRecord("i-0abc", writtenAt), nil }
97 if done, err := publishBoxRecord(ctx, st, "annie-welding", read); !done || err != nil {
98 t.Fatalf("first publish: done=%v err=%v", done, err)
99 }
100 first, err := boxid.Load(ctx, st, "annie-welding")
101 if err != nil {
102 t.Fatal(err)
103 }
104 writtenAt = "2026-10-06T10:00:00Z" // a later run reads a fresh record
105 if done, err := publishBoxRecord(ctx, st, "annie-welding", read); !done || err != nil {
106 t.Fatalf("second publish: done=%v err=%v", done, err)
107 }
108 if again, _ := boxid.Load(ctx, st, "annie-welding"); string(again) != string(first) {
109 t.Fatalf("an existing record was rewritten:\n%s\n%s", first, again)
110 }
111}
112
113// Final review 3: a record already under this box's label that is signed
114// for another instance is someone else's claim on the reference. It is
115// never rewritten, and it is reported once, as done with an error, so the
116// loop logs it loudly.
117func TestPublishBoxRecordReportsARecordForAnotherInstance(t *testing.T) {
118 ctx, st := context.Background(), release.NewDirStore(t.TempDir())
119 if err := boxid.Publish(ctx, st, identityRecord("i-0zzz", "2026-10-06T08:00:00Z")); err != nil {
120 t.Fatal(err)
121 }
122 read := func() (boxid.Record, error) { return identityRecord("i-0abc", "2026-10-06T09:00:00Z"), nil }
123 done, err := publishBoxRecord(ctx, st, "annie-welding", read)
124 if !done || err == nil || !strings.Contains(err.Error(), "i-0zzz") || !strings.Contains(err.Error(), "i-0abc") {
125 t.Fatalf("done=%v err=%v; want done with an error naming both instances", done, err)
126 }
127 // An existing record this box cannot decode is reported the same way.
128 st2 := release.NewDirStore(t.TempDir())
129 if _, err := st2.Put(ctx, boxid.Key("annie-welding"), []byte(`{"v":1}`), ""); err != nil {
130 t.Fatal(err)
131 }
132 if done, err := publishBoxRecord(ctx, st2, "annie-welding", read); !done || err == nil {
133 t.Fatalf("an unreadable existing record: done=%v err=%v", done, err)
134 }
135 // Until this box can read its own identity, it retries.
136 failing := func() (boxid.Record, error) { return boxid.Record{}, errors.New("IMDS down") }
137 if done, err := publishBoxRecord(ctx, st, "annie-welding", failing); done || err == nil {
138 t.Fatalf("own identity unreadable: done=%v err=%v; want a retry", done, err)
139 }
140}
141
142func TestPublishBoxRecordRetriesAReadFailureAndStopsOnABadLabel(t *testing.T) {
143 ctx, st := context.Background(), release.NewDirStore(t.TempDir())
144 failing := func() (boxid.Record, error) { return boxid.Record{}, errors.New("IMDS down") }
145 if done, err := publishBoxRecord(ctx, st, "annie-welding", failing); done || err == nil {
146 t.Fatalf("done=%v err=%v: a read failure is retried", done, err)
147 }
148 if done, err := publishBoxRecord(ctx, st, "Annie.Welding", failing); !done || err == nil {
149 t.Fatalf("done=%v err=%v: a reference that is not a label is not retried", done, err)
150 }
151}
152
153// A store that refuses the read with something other than not-found must
154// not be taken as "no record yet": that would publish over an outage.
155type unreadableBoxStore struct{ release.Store }
156
157func (unreadableBoxStore) Get(context.Context, string) ([]byte, string, error) {
158 return nil, "", errors.New("AccessDenied")
159}
160
161func TestPublishBoxRecordDoesNotPublishWhenItCannotRead(t *testing.T) {
162 ctx := context.Background()
163 st := unreadableBoxStore{release.NewDirStore(t.TempDir())}
164 read := func() (boxid.Record, error) {
165 t.Fatal("IMDS read although the store could not say whether a record exists")
166 return boxid.Record{}, nil
167 }
168 if done, err := publishBoxRecord(ctx, st, "annie-welding", read); done || err == nil {
169 t.Fatalf("done=%v err=%v", done, err)
170 }
171}
172
173func TestRegionHAEnabled(t *testing.T) {
174 // Final review 5: only what strconv.ParseBool reads as true turns it on.
175 for v, want := range map[string]bool{
176 "": false, "0": false, " 0 ": false, "false": false, "FALSE": false, "off": false, "no": false, "yes": false, "garbage": false,
177 "1": true, " 1 ": true, "true": true, "TRUE": true, "t": true,
178 } {
179 if got := regionHAEnabled(func(string) string { return v }); got != want {
180 t.Errorf("CARLOS_REGION_HA=%q: %v, want %v", v, got, want)
181 }
182 }
183}
184