package host import ( "context" "encoding/base64" "errors" "io" "net/http" "strings" "testing" "time" "github.com/carlosframework/platform/internal/boxid" "github.com/carlosframework/platform/internal/release" ) // fakeIMDS answers IMDSv2 in-process: no socket, so it runs in any sandbox. type fakeIMDS struct { doc, sig string status int // for the document and signature; 0 = 200 calls []string } func (f *fakeIMDS) RoundTrip(r *http.Request) (*http.Response, error) { f.calls = append(f.calls, r.Method+" "+r.URL.Path) answer := func(code int, body string) (*http.Response, error) { return &http.Response{StatusCode: code, Body: io.NopCloser(strings.NewReader(body)), Header: http.Header{}, Request: r}, nil } if r.Method == http.MethodPut && r.URL.Path == "/latest/api/token" { return answer(200, "tok\n") } if r.Header.Get("X-aws-ec2-metadata-token") != "tok" { return answer(401, "") } code := f.status if code == 0 { code = 200 } switch r.URL.Path { case imdsIdentityDocument: return answer(code, f.doc) case imdsIdentitySignature: return answer(code, f.sig) } return answer(404, "") } func imdsClient(f *fakeIMDS) *http.Client { return &http.Client{Transport: f} } var recordNow = time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC) // The document is signed byte for byte, so its trailing newline must // survive; the signature arrives wrapped and must not. func TestReadIdentityRecordKeepsTheDocumentsExactBytes(t *testing.T) { doc := "{\n \"accountId\" : \"111122223333\",\n \"instanceId\" : \"i-0abc\"\n}\n" sig := base64.StdEncoding.EncodeToString([]byte(strings.Repeat("s", 256))) wrapped := sig[:64] + "\n" + sig[64:] + "\n" f := &fakeIMDS{doc: doc, sig: wrapped} rec, err := readIdentityRecord(imdsClient(f), "http://imds.test", "annie-welding", recordNow) if err != nil { t.Fatal(err) } got, _ := base64.StdEncoding.DecodeString(rec.IdentityDocument) if string(got) != doc { t.Fatalf("document = %q, want the bytes IMDS served, %q", got, doc) } if rec.IdentitySignature != sig || rec.V != 1 || rec.Box != "annie-welding" || rec.WrittenAt != "2026-10-06T09:00:00Z" { t.Fatalf("record = %+v", rec) } } func TestReadIdentityRecordRefusesWhatIsNotAnAnswer(t *testing.T) { for name, f := range map[string]*fakeIMDS{ "document missing": {doc: "{}", sig: "c2lnCg==", status: 404}, "signature not base64": {doc: "{}", sig: "not base64!"}, "signature empty": {doc: "{}", sig: "\n"}, } { t.Run(name, func(t *testing.T) { if rec, err := readIdentityRecord(imdsClient(f), "http://imds.test", "annie-welding", recordNow); err == nil { t.Fatalf("record = %+v, want an error", rec) } }) } } // identityRecord is a record whose document names instance; the // signature is not checked on this path. func identityRecord(instance, writtenAt string) boxid.Record { doc := base64.StdEncoding.EncodeToString([]byte(`{"accountId":"111122223333","region":"eu-west-1","instanceId":"` + instance + `"}`)) return boxid.Record{V: 1, Box: "annie-welding", IdentityDocument: doc, IdentitySignature: "c2ln", WrittenAt: writtenAt} } func TestPublishBoxRecordWritesOnceAndNeverRewrites(t *testing.T) { ctx, st := context.Background(), release.NewDirStore(t.TempDir()) writtenAt := "2026-10-06T09:00:00Z" read := func() (boxid.Record, error) { return identityRecord("i-0abc", writtenAt), nil } if done, err := publishBoxRecord(ctx, st, "annie-welding", read); !done || err != nil { t.Fatalf("first publish: done=%v err=%v", done, err) } first, err := boxid.Load(ctx, st, "annie-welding") if err != nil { t.Fatal(err) } writtenAt = "2026-10-06T10:00:00Z" // a later run reads a fresh record if done, err := publishBoxRecord(ctx, st, "annie-welding", read); !done || err != nil { t.Fatalf("second publish: done=%v err=%v", done, err) } if again, _ := boxid.Load(ctx, st, "annie-welding"); string(again) != string(first) { t.Fatalf("an existing record was rewritten:\n%s\n%s", first, again) } } // Final review 3: a record already under this box's label that is signed // for another instance is someone else's claim on the reference. It is // never rewritten, and it is reported once, as done with an error, so the // loop logs it loudly. func TestPublishBoxRecordReportsARecordForAnotherInstance(t *testing.T) { ctx, st := context.Background(), release.NewDirStore(t.TempDir()) if err := boxid.Publish(ctx, st, identityRecord("i-0zzz", "2026-10-06T08:00:00Z")); err != nil { t.Fatal(err) } read := func() (boxid.Record, error) { return identityRecord("i-0abc", "2026-10-06T09:00:00Z"), nil } done, err := publishBoxRecord(ctx, st, "annie-welding", read) if !done || err == nil || !strings.Contains(err.Error(), "i-0zzz") || !strings.Contains(err.Error(), "i-0abc") { t.Fatalf("done=%v err=%v; want done with an error naming both instances", done, err) } // An existing record this box cannot decode is reported the same way. st2 := release.NewDirStore(t.TempDir()) if _, err := st2.Put(ctx, boxid.Key("annie-welding"), []byte(`{"v":1}`), ""); err != nil { t.Fatal(err) } if done, err := publishBoxRecord(ctx, st2, "annie-welding", read); !done || err == nil { t.Fatalf("an unreadable existing record: done=%v err=%v", done, err) } // Until this box can read its own identity, it retries. failing := func() (boxid.Record, error) { return boxid.Record{}, errors.New("IMDS down") } if done, err := publishBoxRecord(ctx, st, "annie-welding", failing); done || err == nil { t.Fatalf("own identity unreadable: done=%v err=%v; want a retry", done, err) } } func TestPublishBoxRecordRetriesAReadFailureAndStopsOnABadLabel(t *testing.T) { ctx, st := context.Background(), release.NewDirStore(t.TempDir()) failing := func() (boxid.Record, error) { return boxid.Record{}, errors.New("IMDS down") } if done, err := publishBoxRecord(ctx, st, "annie-welding", failing); done || err == nil { t.Fatalf("done=%v err=%v: a read failure is retried", done, err) } if done, err := publishBoxRecord(ctx, st, "Annie.Welding", failing); !done || err == nil { t.Fatalf("done=%v err=%v: a reference that is not a label is not retried", done, err) } } // A store that refuses the read with something other than not-found must // not be taken as "no record yet": that would publish over an outage. type unreadableBoxStore struct{ release.Store } func (unreadableBoxStore) Get(context.Context, string) ([]byte, string, error) { return nil, "", errors.New("AccessDenied") } func TestPublishBoxRecordDoesNotPublishWhenItCannotRead(t *testing.T) { ctx := context.Background() st := unreadableBoxStore{release.NewDirStore(t.TempDir())} read := func() (boxid.Record, error) { t.Fatal("IMDS read although the store could not say whether a record exists") return boxid.Record{}, nil } if done, err := publishBoxRecord(ctx, st, "annie-welding", read); done || err == nil { t.Fatalf("done=%v err=%v", done, err) } } func TestRegionHAEnabled(t *testing.T) { // Final review 5: only what strconv.ParseBool reads as true turns it on. for v, want := range map[string]bool{ "": false, "0": false, " 0 ": false, "false": false, "FALSE": false, "off": false, "no": false, "yes": false, "garbage": false, "1": true, " 1 ": true, "true": true, "TRUE": true, "t": true, } { if got := regionHAEnabled(func(string) string { return v }); got != want { t.Errorf("CARLOS_REGION_HA=%q: %v, want %v", v, got, want) } } }