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/json"
6 "errors"
7 "os"
8 "path/filepath"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/activator"
13 "github.com/carlosframework/platform/internal/hostclaim"
14 "github.com/carlosframework/platform/internal/provision"
15 "github.com/carlosframework/platform/internal/registry"
16 "github.com/carlosframework/platform/internal/release"
17)
18
19func plan14ProofFixture(t *testing.T) (*proofRootEnv, registry.Route, provision.Record, *proofBindingSource) {
20 t.Helper()
21 e := newProofRootEnv(t, provePolicy)
22 rec := provision.Record{Schema: 1, Host: observedHost, Account: "bab", App: "console", Kind: "instance",
23 Unit: "console.service", Addr: "127.0.0.1:4000", Channel: "canary/console", Pin: "v1", Health: &provision.Health{Path: "/ready"}}
24 plan14PutRecord(t, e.store, rec)
25 health, err := json.Marshal(rec.Health.WithDefaults())
26 if err != nil {
27 t.Fatal(err)
28 }
29 rt := registry.Route{Host: rec.Host, Account: rec.Account, App: rec.App, Kind: rec.Kind, Unit: rec.Unit,
30 Addr: rec.Addr, Health: string(health), Channel: rec.Channel, Status: "ready"}
31 if err := e.root.reg.Upsert(rt); err != nil {
32 t.Fatal(err)
33 }
34 unitsDir := t.TempDir()
35 fragment := filepath.Join(unitsDir, rec.Unit)
36 if err := os.WriteFile(fragment, []byte("[Service]\nExecStart="+release.LiveLink(e.root.obs.optDir, rec.Host)+"\n"), 0644); err != nil {
37 t.Fatal(err)
38 }
39 f := e.root.obs.sys.(*fakeUnits)
40 f.shows[0].Name, f.shows[0].FragmentPath = rec.Unit, fragment
41 f.exe = map[int]string{7: release.VersionPath(e.root.obs.optDir, rec.Account, rec.Pin, rec.App)}
42 source := &proofBindingSource{store: e.store, sys: f, optDir: e.root.obs.optDir, openFile: os.Open, unitDirs: []string{unitsDir}}
43 e.root.obs.resolve = source.resolve
44 e.root.obs.ready = func(registry.Route) func() error { return func() error { return nil } }
45 return e, rt, rec, source
46}
47
48func plan14PutRecord(t *testing.T, s release.Store, rec provision.Record) {
49 t.Helper()
50 key := provision.RecordKey(rec.Account, rec.App, rec.Host)
51 _, etag, err := s.Get(context.Background(), key)
52 if err != nil && !errors.Is(err, os.ErrNotExist) {
53 t.Fatal(err)
54 }
55 body, err := json.Marshal(rec)
56 if err != nil {
57 t.Fatal(err)
58 }
59 if _, err := s.Put(context.Background(), key, body, etag); err != nil {
60 t.Fatal(err)
61 }
62}
63
64func TestPlan14ProofObservationRejectsForgedRegistry(t *testing.T) {
65 t.Parallel()
66 mutations := map[string]func(*registry.Route){
67 "account": func(r *registry.Route) { r.Account = "bap" },
68 "app": func(r *registry.Route) { r.App = "other" },
69 "host": func(r *registry.Route) { r.Host = "other.example.com" },
70 "unit": func(r *registry.Route) { r.Unit = "other.service" },
71 "socket": func(r *registry.Route) { r.Socket = "/run/forged.sock" },
72 "addr": func(r *registry.Route) { r.Addr = "" },
73 "addrs": func(r *registry.Route) { r.Addrs = "127.0.0.1:1" },
74 "health nil": func(r *registry.Route) { r.Health = "" },
75 "health disabled": func(r *registry.Route) { r.Health = "null" },
76 "health alternate": func(r *registry.Route) { r.Health = `{"path":"/forged"}` },
77 "all upstreams empty": func(r *registry.Route) { r.Addr, r.Addrs, r.Socket, r.Health = "", "", "", "" },
78 }
79 for name, mutate := range mutations {
80 t.Run(name, func(t *testing.T) {
81 e, rt, _, _ := plan14ProofFixture(t)
82 mutate(&rt)
83 if err := e.root.reg.Delete(observedHost); err != nil {
84 t.Fatal(err)
85 }
86 if err := e.root.reg.Upsert(rt); err != nil {
87 t.Fatal(err)
88 }
89 if got := e.root.observeAndRecord(rt, 15*time.Minute); got.err == nil {
90 t.Fatal("forged route recorded as healthy")
91 }
92 e.root.run(context.Background())
93 if err := e.root.obs.recordSettled(e.bake)(rt); err == nil {
94 t.Fatal("forged settled route accepted")
95 }
96 keys, err := e.store.List(context.Background(), "apps/")
97 if err != nil || len(keys) != 1 {
98 t.Fatalf("unexpected proof writes: %v, %v", keys, err)
99 }
100 for _, acct := range []string{"bab", "bap"} {
101 for _, app := range []string{"console", "other"} {
102 if got := release.ReadHealthy(e.bake, acct, app); len(got) != 0 {
103 t.Fatalf("forged health %s/%s: %+v", acct, app, got)
104 }
105 }
106 }
107 })
108 }
109 t.Run("canonical failing readiness", func(t *testing.T) {
110 e, rt, _, _ := plan14ProofFixture(t)
111 e.root.obs.ready = func(r registry.Route) func() error {
112 if r.Addr == "" {
113 return nil
114 }
115 return func() error { return os.ErrPermission }
116 }
117 e.root.run(context.Background())
118 if e.root.obs.recordSettled(e.bake)(rt) == nil || len(release.ReadHealthy(e.bake, "bab", "console")) != 0 || e.proof(t) != nil {
119 t.Fatal("failed canonical readiness authorized health")
120 }
121 })
122 t.Run("canonical success", func(t *testing.T) {
123 e, rt, _, _ := plan14ProofFixture(t)
124 e.root.run(context.Background())
125 if err := e.root.obs.recordSettled(e.bake)(rt); err != nil {
126 t.Fatal(err)
127 }
128 if len(release.ReadHealthy(e.bake, "bab", "console")) != 1 || e.proof(t) == nil {
129 t.Fatal("canonical healthy build was not recorded and proved")
130 }
131 })
132}
133
134func TestPlan14ProofBindingChangesAfterObservation(t *testing.T) {
135 t.Parallel()
136 e, _, rec, _ := plan14ProofFixture(t)
137 obs := e.root.recordLive(context.Background())
138 rec.Health.Path = "/changed"
139 plan14PutRecord(t, e.store, rec)
140 e.root.mintAndLog(context.Background(), obs)
141 if e.proof(t) != nil {
142 t.Fatal("stale binding minted a proof")
143 }
144 rt, err := e.root.reg.Get(rec.Host)
145 if err != nil {
146 t.Fatal(err)
147 }
148 health, err := json.Marshal(rec.Health.WithDefaults())
149 if err != nil {
150 t.Fatal(err)
151 }
152 rt.Health = string(health)
153 if err := e.root.reg.Upsert(*rt); err != nil {
154 t.Fatal(err)
155 }
156 e.root.run(context.Background())
157 if e.proof(t) == nil {
158 t.Fatal("new successful observation did not mint")
159 }
160 t.Run("etag only", func(t *testing.T) {
161 e, _, rec, _ := plan14ProofFixture(t)
162 obs := e.root.recordLive(context.Background())
163 rec.RecordSeq++
164 plan14PutRecord(t, e.store, rec)
165 e.root.obs.ready = func(registry.Route) func() error { return func() error { return os.ErrPermission } }
166 e.root.mintAndLog(context.Background(), obs)
167 if e.proof(t) != nil {
168 t.Fatal("changed ETag reused cached observation")
169 }
170 })
171}
172
173func TestPlan14OrdinaryInstanceBinding(t *testing.T) {
174 t.Parallel()
175 e, rt, rec, source := plan14ProofFixture(t)
176 rec.Addr, rec.Unit = "", ""
177 plan14PutRecord(t, e.store, rec)
178 rt.Addr, rt.Unit, rt.Socket, rt.Backing = "", "", provision.DefaultSocket(rec.Host), registry.BackingUnit
179 if err := e.root.reg.Upsert(rt); err != nil {
180 t.Fatal(err)
181 }
182 u := source.sys.(*fakeUnits)
183 u.shows[0].Name = activator.UnitName(rec.Host)
184 if got := e.root.observeAndRecord(rt, 0); got.err == nil {
185 t.Fatal("ordinary record recorded root health")
186 }
187 if got := release.ReadHealthy(e.bake, rt.Account, rt.App); len(got) != 0 {
188 t.Fatalf("ordinary health: %+v", got)
189 }
190 rt.Unit = "other.service"
191 if got := e.root.observeAndRecord(rt, 0); got.err == nil {
192 t.Fatal("forged ordinary unit accepted")
193 }
194 rec.Unit = "other.service"
195 plan14PutRecord(t, e.store, rec)
196 if _, err := source.forHost(context.Background(), rt.Host); err == nil {
197 t.Fatal("record overrides ordinary naming")
198 }
199}
200
201func TestPlan14RootRestartDispatchFromRecord(t *testing.T) {
202 t.Parallel()
203 e, rt, rec, source := plan14ProofFixture(t)
204 rec.Unit = ""
205 rt.Unit = ""
206 plan14PutRecord(t, e.store, rec)
207 needs, err := source.needsRestart(context.Background(), rt)
208 if err != nil || needs {
209 t.Fatalf("unmanaged proxy restart: %v %v", needs, err)
210 }
211 rec.Unit = "console.service"
212 plan14PutRecord(t, e.store, rec)
213 if _, err := source.needsRestart(context.Background(), rt); err == nil {
214 t.Fatal("cleared candidate unit suppressed authoritative restart")
215 }
216 rt.Unit = rec.Unit
217 needs, err = source.needsRestart(context.Background(), rt)
218 if err != nil || !needs {
219 t.Fatalf("named service skipped: %v %v", needs, err)
220 }
221}
222
223func TestPlan14ProofClaimBinding(t *testing.T) {
224 t.Parallel()
225 e, rt, rec, s := plan14ProofFixture(t)
226 observed := hostclaim.Observed{Host: rec.Host, ETag: "claim-one", Claim: &hostclaim.Claim{Host: rec.Host, Holder: &hostclaim.Holder{Key: provision.RecordKey(rec.Account, rec.App, rec.Host), Account: rec.Account, App: rec.App, Incarnation: "inc", Stamp: 1, Session: 1, WriterBox: "box"}, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: "box"}}}
227 s.claim = func(context.Context, string) (hostclaim.Observed, error) { return observed, nil }
228 s.box = func(context.Context) (string, error) { return "box", nil }
229 cached := e.root.recordLive(context.Background())
230 if cached.byHost[rt.Host].err != nil {
231 t.Fatal(cached.byHost[rt.Host].err)
232 }
233 observed.Claim.Holder.App = "other"
234 e.root.mintAndLog(context.Background(), cached)
235 if e.proof(t) != nil {
236 t.Fatal("changed holder minted proof")
237 }
238 if _, err := s.forHost(context.Background(), rt.Host); err == nil {
239 t.Fatal("cross-app holder accepted")
240 }
241 observed.Claim.Holder.App = rec.App
242 observed.Claim.Holder.WriterBox = "other-box"
243 if _, err := s.forHost(context.Background(), rt.Host); err == nil {
244 t.Fatal("foreign writer accepted")
245 }
246}
247