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 "crypto/ed25519"
5 "encoding/json"
6 "errors"
7 "os"
8 "path/filepath"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/hostactors"
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/hostfloor"
15 "github.com/carlosframework/platform/internal/release"
16)
17
18// The regional enrollment check runs on the production roster adapter: a
19// roster revision bump (a create's actor hold is one) passes, and a
20// quarantine and readmission since startup still refuses.
21func TestClaimRegionalEnrollmentSurvivesRosterRevision(t *testing.T) {
22 key := ed25519.NewKeyFromSeed(make([]byte, 32))
23 s := release.NewDirStore(t.TempDir())
24 bucket := "deployment"
25 if err := hostfloor.Publish(t.Context(), s, release.RootSigner(key), bucket, hostfloor.Floor{Min: 1, MinRelease: "v0.1.0"}); err != nil {
26 t.Fatal(err)
27 }
28 raw, _, _ := s.Get(t.Context(), hostfloor.Key)
29 auth, _, _ := s.Get(t.Context(), hostfloor.AuthKey)
30 dir := t.TempDir()
31 body, _ := json.Marshal(hostfloor.Policy{Schema: 1, DeploymentBucket: bucket, Mode: "enforced", Floor: raw, Authorization: auth})
32 if err := os.WriteFile(filepath.Join(dir, "policy.json"), body, 0600); err != nil {
33 t.Fatal(err)
34 }
35 identity := handoverRecoveryIdentity(t, hostbox.Actor{Box: "box", Enrollment: strings.Repeat("a", 32), Region: "ie", ControlBucket: bucket, Revision: 1, Binding: "1"})
36 row := hostactors.Box{BoxID: "box", Enrollment: strings.Repeat("a", 32), Namespace: identity.Namespace, Label: "box", Region: "ie", Pool: "default", Cloud: hostactors.Cloud{Provider: "aws", Account: identity.Account, Region: identity.AWSRegion, InstanceID: "i-0abc"}, EvidenceSHA256: strings.Repeat("c", 64), State: "active"}
37 roster := hostactors.Roster{V: 1, DeploymentBucket: bucket, Revision: 1, RequestLimitNS: int64(hostbox.RequestLimit), Publication: "published", FloorSHA256: hostfloor.Digest(raw), Boxes: map[string]hostactors.Box{"box": row}, Consoles: map[string]hostactors.Console{}, Holds: map[string]hostactors.Reservation{}, Activations: map[string]hostactors.Reservation{}}
38 write := func() {
39 t.Helper()
40 wire, err := release.SignRootEnvelope(release.RootSigner(key), roster)
41 if err != nil {
42 t.Fatal(err)
43 }
44 _, etag, _ := s.Get(t.Context(), hostactors.Key)
45 if _, err = s.Put(t.Context(), hostactors.Key, wire, etag); err != nil {
46 t.Fatal(err)
47 }
48 }
49 write()
50 identity.Roster = productionClaimRoster{Directory: hostactors.Directory{Store: s, PublicKey: key.Public().(ed25519.PublicKey), Bucket: bucket}, DataBucket: identity.Bucket, FloorDir: dir}
51 actor, err := identity.Verify(t.Context())
52 if err != nil {
53 t.Fatal(err)
54 }
55 e := claimRegionalEnrollment{identity: identity, actor: actor}
56 check := func(step string, want error) {
57 t.Helper()
58 if err := e.VerifyRegional(t.Context(), actor.Region, actor.Bucket, hostbox.RequestLimit); !errors.Is(err, want) && !(want == nil && err == nil) {
59 t.Fatalf("%s: VerifyRegional = %v, want %v", step, err, want)
60 }
61 }
62 check("startup", nil)
63
64 roster.Revision++
65 write()
66 check("roster revision bump", nil)
67
68 row.State, row.QuarantineRevision, row.LossIDs = "quarantined", 1, []string{"loss"}
69 roster.Boxes["box"], roster.Revision = row, roster.Revision+1
70 write()
71 check("quarantined", hostbox.ErrChanged)
72
73 row.State = "active"
74 roster.Boxes["box"], roster.Revision = row, roster.Revision+1
75 write()
76 check("readmitted since startup", hostbox.ErrChanged)
77}
78
79// Each enrollment field the roster carries still refuses when it changes
80// under a running assembly; only the revision bump passes.
81func TestClaimRegionalEnrollmentFieldChanges(t *testing.T) {
82 base := hostbox.Actor{Box: "box", Enrollment: "enrolled", Region: "region", FleetHomeRegion: "region", ControlBucket: "control", Revision: 1, Binding: "1"}
83 identity := handoverRecoveryIdentity(t, base)
84 actor, err := identity.Verify(t.Context())
85 if err != nil {
86 t.Fatal(err)
87 }
88 e := claimRegionalEnrollment{identity: identity, actor: actor}
89 for name, edit := range map[string]func(*hostbox.Actor){
90 "revision": func(a *hostbox.Actor) { a.Revision++ },
91 "eligible": func(a *hostbox.Actor) { a.Eligible = false },
92 "quarantined": func(a *hostbox.Actor) { a.Quarantined = true },
93 "binding": func(a *hostbox.Actor) { a.Binding += "-changed" },
94 "box": func(a *hostbox.Actor) { a.Box = "other" },
95 "enrollment": func(a *hostbox.Actor) { a.Enrollment = "re-enrolled" },
96 "region": func(a *hostbox.Actor) { a.Region = "other" },
97 "bucket": func(a *hostbox.Actor) { a.Bucket = "other" },
98 "namespace": func(a *hostbox.Actor) { a.Namespace = "other" },
99 "control bucket": func(a *hostbox.Actor) { a.ControlBucket = "other" },
100 "fleet home region": func(a *hostbox.Actor) { a.FleetHomeRegion = "other" },
101 "cloud account": func(a *hostbox.Actor) { a.Cloud.Account = "444455556666" },
102 "cloud region": func(a *hostbox.Actor) { a.Cloud.Region = "eu-west-2" },
103 "cloud instance": func(a *hostbox.Actor) { a.Cloud.InstanceID = "i-0def" },
104 } {
105 t.Run(name, func(t *testing.T) {
106 changed := identity
107 row := actor
108 row.HomeRegion = ""
109 edit(&row)
110 changed.Roster = handoverRecoveryRoster{row}
111 e := e
112 e.identity = changed
113 err := e.VerifyRegional(t.Context(), actor.Region, actor.Bucket, hostbox.RequestLimit)
114 if name == "revision" {
115 if err != nil {
116 t.Fatalf("revision bump refused: %v", err)
117 }
118 return
119 }
120 // A new instance ID fails Verify's signed cloud evidence first,
121 // with that check's own error; every other change is ErrChanged.
122 if err == nil || (name != "cloud instance" && !errors.Is(err, hostbox.ErrChanged)) {
123 t.Fatalf("changed enrollment: %v, want ErrChanged", err)
124 }
125 })
126 }
127}
128