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 "context"
5 "crypto/ed25519"
6 "crypto/rand"
7 "encoding/json"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/hostactors"
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/hostclaim"
15 "github.com/carlosframework/platform/internal/hostevidence"
16 "github.com/carlosframework/platform/internal/release"
17)
18
19func TestBothNewParksReuseAuthenticatedPrePurgeStopReceipt(t *testing.T) {
20 ctx := context.Background()
21 pub, key, err := ed25519.GenerateKey(rand.Reader)
22 if err != nil {
23 t.Fatal(err)
24 }
25 boxPub, boxKey, err := ed25519.GenerateKey(rand.Reader)
26 if err != nil {
27 t.Fatal(err)
28 }
29 store := release.NewDirStore(t.TempDir())
30 at := time.Date(2026, 10, 7, 0, 0, 0, 0, time.UTC)
31 enrollment := strings.Repeat("a", 32)
32 roster := hostactors.Roster{V: 1, DeploymentBucket: "bucket", Revision: 3, RequestLimitNS: int64(hostbox.RequestLimit), Regions: map[string]hostactors.Region{"ie": {}}, Boxes: map[string]hostactors.Box{
33 "box": {CensusPublicKey: boxPub, Enrollment: enrollment, Namespace: "platform", BoxID: "box", Label: "box", Region: "ie", Pool: "default", Cloud: hostactors.Cloud{Provider: "aws", Account: "123", Region: "ie", InstanceID: "i-box"}, EvidenceSHA256: strings.Repeat("c", 64), State: "active"},
34 }, Consoles: map[string]hostactors.Console{}, Holds: map[string]hostactors.Reservation{}, Activations: map[string]hostactors.Reservation{}}
35 signed, err := release.SignRootEnvelope(release.RootSigner(key), roster)
36 if err != nil {
37 t.Fatal(err)
38 }
39 if _, err = store.Put(ctx, hostactors.Key, signed, ""); err != nil {
40 t.Fatal(err)
41 }
42 inc := strings.Repeat("b", 64)
43 obs := hostclaim.Observed{Host: "x.example", ETag: "claim-etag", Claim: &hostclaim.Claim{V: 2, Host: "x.example", Gen: 10, Holder: &hostclaim.Holder{Incarnation: inc, Stamp: 4, Session: 5, WriterBox: "box"}, Op: hostclaim.Op{Kind: hostclaim.KindParking, ID: "purge", Exec: "box"}}}
44 receipt := StopReceipt{Region: "ie", ClaimETag: "original-claim-etag", ObservedBootID: "boot-after-recovery", V: 1, ID: "receipt", Host: obs.Host, Incarnation: inc, InstallationStamp: 4, WriterSession: 5, OperationID: "purge", Phase: "stopping", ObligationID: "pre-teardown-stop", Box: "box", Enrollment: enrollment, BootID: "boot", ControllerID: "controller", ClaimGen: 9, DirectoryRevision: "3", Kind: "stopped", EvidenceAt: at, FenceID: "fence", InventorySHA256: strings.Repeat("d", 64), LocalJournalSHA256: strings.Repeat("e", 64), ProducerDrainDigest: strings.Repeat("f", 64), CgroupIdentities: []string{"cgroup"}, RequestLimitNS: int64(hostbox.RequestLimit)}
45 raw, err := json.Marshal(receipt)
46 if err != nil {
47 t.Fatal(err)
48 }
49 receiptKey := "control/hosts/x.example/evidence/purge/box/receipt.json"
50 if err = hostevidence.Publish(ctx, store, boxKey, receiptKey, raw); err != nil {
51 t.Fatal(err)
52 }
53 authority := ProductionAuthority{Store: store, Directory: hostactors.Directory{Store: store, PublicKey: pub, Bucket: "bucket"}, Now: func() time.Time { return at.Add(time.Hour) }}
54 for _, consumer := range []struct{ id, target string }{{"new-instance-park", "instances/x.example/" + inc}, {"new-staging-park", "moves/x.example"}} {
55 ref, err := authority.chooseEvidence(ctx, obs, consumer.id, "purge", "box")
56 if err != nil {
57 t.Fatalf("%s did not reuse original stop receipt: %v", consumer.id, err)
58 }
59 if ref.ObligationID != consumer.id || ref.SourceObligationID != "pre-teardown-stop" || ref.Key != receiptKey {
60 t.Fatalf("lost source/consumer binding: %+v", ref)
61 }
62 scope, err := retirementScope(obs.Host, inc, consumer.target, map[string]hostclaim.RetirementEvidenceRef{"box": ref})
63 if err != nil || scope.Location != consumer.target || !scope.EvidenceAt.Equal(at) {
64 t.Fatalf("scope = %+v, %v", scope, err)
65 }
66 for _, field := range []string{"region", "claim_etag", "observed_boot_id", "request_limit_ns"} {
67 var changed map[string]json.RawMessage
68 if err := json.Unmarshal(raw, &changed); err != nil {
69 t.Fatal(err)
70 }
71 changed[field] = json.RawMessage(`"changed"`)
72 if field == "request_limit_ns" {
73 changed[field] = json.RawMessage(`1`)
74 }
75 tampered, err := json.Marshal(changed)
76 if err != nil {
77 t.Fatal(err)
78 }
79 _, etag, err := store.Get(ctx, receiptKey)
80 if err != nil {
81 t.Fatal(err)
82 }
83 etag, err = store.Put(ctx, receiptKey, tampered, etag)
84 if err != nil {
85 t.Fatal(err)
86 }
87 forged := ref
88 forged.SHA256 = evidenceDigest(tampered)
89 if _, err := authority.verifyEvidence(ctx, obs, consumer.id, "purge", forged, false); err == nil {
90 t.Fatalf("accepted tampered %s with original signature", field)
91 }
92 if _, err := store.Put(ctx, receiptKey, raw, etag); err != nil {
93 t.Fatal(err)
94 }
95 }
96 ref.SourceObligationID = "different-source"
97 if _, err = authority.verifyEvidence(ctx, obs, consumer.id, "purge", ref, false); err == nil {
98 t.Fatal("accepted a substituted producer obligation")
99 }
100 }
101}
102