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/base64"
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/hostfloor"
13 "github.com/carlosframework/platform/internal/provision"
14 "github.com/carlosframework/platform/internal/registry"
15 "github.com/carlosframework/platform/internal/release"
16 "github.com/carlosframework/platform/internal/rootstate"
17)
18
19func TestPlan14NamedUnitsWithoutDeploymentRecord(t *testing.T) {
20 if !plan14RootFixture(t) {
21 return
22 }
23 for _, scenario := range []string{"console bootstrap", "console bootstrap invalid capture", "console update", "console crash", "operator update", "forged unit", "changed list", "untrusted list", "missing dropin", "captured target changed"} {
24 t.Run(scenario, func(t *testing.T) {
25 e, rt, u, c, pub := plan14AdoptionFixture(t)
26 if err := e.store.Delete(t.Context(), provision.RecordKey(rt.Account, rt.App, rt.Host)); err != nil {
27 t.Fatal(err)
28 }
29 rt.Account = ""
30 rt.Addr = "127.0.0.1:8090"
31 rt.Health = `{"path":"/healthz","timeout":"2s","interval":"30s","grace":"10s","failures":3}`
32 if err := e.root.reg.Upsert(rt); err != nil {
33 t.Fatal(err)
34 }
35 if err := os.MkdirAll("/var/lib", 0755); err != nil {
36 t.Fatal(err)
37 }
38 if err := rootstate.EnsurePublicDir(release.RootHealthyDir); err != nil {
39 t.Fatal(err)
40 }
41 oldHealth := release.ReadHealthy(e.bake, "bab", "console")
42 for _, b := range oldHealth {
43 if strings.HasPrefix(scenario, "console bootstrap") {
44 continue
45 }
46 if err := release.RecordHealthy(release.RootHealthyDir, "", "console", b); err != nil {
47 t.Fatal(err)
48 }
49 }
50 t.Cleanup(func() {
51 if err := os.RemoveAll(release.RootHealthyDir); err != nil {
52 t.Error(err)
53 }
54 })
55 e.bake = release.RootHealthyDir
56 s := productionProofBindingSource(e.store, u, u.opt)
57 s.namedPath = filepath.Join(t.TempDir(), "named-units.json")
58 s.unitDirs = []string{filepath.Dir(u.props.FragmentPath)}
59 s.now = c.now
60 c.attempts = productionRootRestartChecks(u, u.opt).attempts
61 c.resolve, c.host, c.rollback, c.prepareRollback, c.dispatch, c.targetFile = s.resolve, s.forHost, s.rollback, s.prepareRollback, s.needsRestart, rootstate.OpenTrustedFile
62 e.root.obs.resolve = s.resolve
63 put := func(path string, body []byte) {
64 t.Helper()
65 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
66 t.Fatal(err)
67 }
68 if err := os.WriteFile(path, body, 0644); err != nil {
69 t.Fatal(err)
70 }
71 makeProofAncestorsPublic(t, path)
72 }
73 if scenario == "operator update" || scenario == "changed list" || scenario == "untrusted list" {
74 u.props.Name = "operator.service"
75 rt.Unit = u.props.Name
76 raw, err := json.Marshal(struct {
77 Schema int `json:"schema"`
78 Units []namedUnit `json:"units"`
79 }{1, []namedUnit{{Host: rt.Host, App: rt.App, Unit: rt.Unit, Addr: rt.Addr, Health: &provision.Health{Path: "/healthz"}}}})
80 if err != nil {
81 t.Fatal(err)
82 }
83 put(s.namedPath, raw)
84 } else if scenario != "forged unit" {
85 marker := filepath.Join(u.opt, "system", "guard", "console-ready.json")
86 put(marker, []byte(release.LiveLink(u.opt, rt.Host)))
87 dropin := filepath.Join(s.unitDirs[0], "console.service.d", "90-host-floor.conf")
88 put(dropin, []byte(hostfloor.ConsoleGuardDropIn(release.LiveLink(u.opt, rt.Host))))
89 u.props.DropInPaths = []string{dropin}
90 u.props.ExecPath = "/opt/carlos/system/guard/carlos"
91 put(u.props.ExecPath, []byte("root guard"))
92 }
93 if strings.HasPrefix(scenario, "console bootstrap") {
94 public, key, err := ed25519.GenerateKey(nil)
95 if err != nil {
96 t.Fatal(err)
97 }
98 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(public))
99 rel := release.SystemRelease{Version: "v1", ReleaseTag: "v1.0.0", MovesProtocol: 1, Artifacts: map[string]release.SystemArtifact{release.AdoptPlatform(): {Key: release.SystemArtifactKey("v1", release.AdoptPlatform()), SHA256: strings.Repeat("a", 64)}}, ConsoleArtifacts: map[string]release.SystemArtifact{release.AdoptPlatform(): {Key: "bootstrap/console/v1/carlos-" + release.AdoptPlatform(), SHA256: oldHealth[0].SHA256}}}
100 if _, err := release.PromoteSystem(t.Context(), e.store, release.RootSigner(key), "router", "canary", rel, release.SystemPromoteOptions{}); err != nil {
101 t.Fatal(err)
102 }
103 wire, _, err := e.store.Get(t.Context(), release.SystemPointerKey("router", "canary"))
104 if err != nil {
105 t.Fatal(err)
106 }
107 put(filepath.Join(u.opt, "system", "guard", "identities", "v1.0.0.json"), wire)
108 }
109 makeProofAncestorsPublic(t, u.props.FragmentPath)
110 makeProofAncestorsPublic(t, u.exe)
111 makeProofAncestorsPublic(t, s.namedPath)
112 if scenario != "forged unit" {
113 if _, err := s.resolve(t.Context(), rt); err != nil {
114 t.Fatalf("baseline: %v", err)
115 }
116 }
117 if scenario == "console bootstrap invalid capture" {
118 put(filepath.Join(u.opt, "system", "guard", "identities", "v1.0.0.json"), []byte(`{"signature":"forged"}`))
119 if _, err := s.resolve(t.Context(), rt); err != nil {
120 t.Fatal("root live pointer must remain authoritative without installation history", err)
121 }
122 if len(u.effects) != 0 {
123 t.Fatal("invalid capture restarted console")
124 }
125 return
126 }
127 if strings.HasPrefix(scenario, "console bootstrap") {
128 if err := e.root.obs.recordSettled(e.bake)(rt); err != nil {
129 t.Fatal("initial health", err)
130 }
131 }
132 switch scenario {
133 case "console crash":
134 u.crashNew = true
135 case "untrusted list":
136 if err := os.Chmod(s.namedPath, 0666); err != nil {
137 t.Fatal(err)
138 }
139 case "missing dropin":
140 u.props.DropInPaths = nil
141 case "changed list":
142 u.crashNew = true
143 u.afterEffect = func() {
144 raw, err := os.ReadFile(s.namedPath)
145 if err != nil {
146 t.Fatal(err)
147 }
148 put(s.namedPath, append(raw, ' '))
149 }
150 case "captured target changed":
151 u.afterRestart = func() { put(release.VersionPath(u.opt, "", "v2", "console"), []byte("changed after capture")) }
152 }
153 // Registry metadata cannot select a unit or the readiness inputs.
154 rt.Unit = "forged.service"
155 rt.Addr = "127.0.0.1:1"
156 rt.Health = ""
157 if err := e.root.reg.Upsert(rt); err != nil {
158 t.Fatal(err)
159 }
160 c.ready = func(got registry.Route) func() error {
161 if got.Unit == rt.Unit || got.Addr != "127.0.0.1:8090" || got.Health == "" {
162 return func() error { return os.ErrPermission }
163 }
164 return func() error { return nil }
165 }
166 e.root.obs.ready = c.ready
167 forward := rootRestartRouteWith(e.root.reg, u, u.opt, func(string) string { return "5s" }, e.root.obs.recordSettled(e.bake), c)
168 rollback := rollbackUnitRestart(e.root.reg, u, u.opt, func(string) string { return "5s" }, c)
169 var rollbackErr error
170 err := release.Pass(t.Context(), e.root.reg, e.store, pub, u.opt, forward, release.BakeDir(e.bake), release.WithRestartFailurePropagation(), release.WithRollbackRestart(func(host string) error { rollbackErr = rollback(host); return rollbackErr }))
171 switch scenario {
172 case "console bootstrap", "console update", "operator update":
173 if err != nil {
174 t.Fatal(err)
175 }
176 if len(u.effects) != 1 || u.exe != release.VersionPath(u.opt, "", "v2", "console") {
177 t.Fatal(u.effects, u.exe)
178 }
179 if got := release.ReadHealthy(e.bake, "", "console"); len(got) != 2 {
180 t.Fatal("missing health", got)
181 }
182 if err := forward(rt.Host); err != nil {
183 t.Fatal("explicit named-unit restart", err)
184 }
185 if len(u.effects) != 2 {
186 t.Fatal("missing explicit restart", u.effects)
187 }
188 if got := release.ReadHealthy(e.bake, "", "console"); len(got) != 2 {
189 t.Fatal("explicit restart changed digest history", got)
190 }
191 case "console crash":
192 if err == nil || rollbackErr != nil || len(u.effects) != 3 || u.exe != release.VersionPath(u.opt, "", "v1", "console") {
193 t.Fatal(err, rollbackErr, u.effects, u.exe)
194 }
195 if got := release.ReadHealthy(e.bake, "", "console"); len(got) != 1 {
196 t.Fatal("crash recorded healthy", got)
197 }
198 case "forged unit", "untrusted list", "missing dropin":
199 if err == nil || len(u.effects) != 0 {
200 t.Fatal("forged authority restarted", err, u.effects)
201 }
202 default:
203 if err == nil {
204 t.Fatal("mutation succeeded")
205 }
206 }
207 })
208 }
209}
210