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 "go/ast"
5 "go/parser"
6 "go/token"
7 "io/fs"
8 "os"
9 "path/filepath"
10 "slices"
11 "sort"
12 "strings"
13 "testing"
14)
15
16// Ruling 6 for step 8 readiness: nothing changes an effect on a claims-off
17// or no-census box. Every new or widened census entry point is reached only
18// from claim-mode code. journal-migrate refuses without CARLOS_HOST_CLAIM
19// (RunSystemJournalMigrate), the root migration runs only under it, the
20// claim assembly and root inventory startup exist only with claims on, and
21// the retry directory is prepared only with a census or claim units on. This
22// pins the callers, so a new claims-off caller fails here first.
23func TestStep8CensusChangesReachOnlyClaimMode(t *testing.T) {
24 callers := map[string][]string{}
25 fset := token.NewFileSet()
26 entries, err := os.ReadDir(".")
27 if err != nil {
28 t.Fatal(err)
29 }
30 for _, e := range entries {
31 name := e.Name()
32 if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
33 continue
34 }
35 f, err := parser.ParseFile(fset, name, nil, 0)
36 if err != nil {
37 t.Fatal(err)
38 }
39 for _, decl := range f.Decls {
40 fn, ok := decl.(*ast.FuncDecl)
41 if !ok || fn.Body == nil {
42 continue
43 }
44 ast.Inspect(fn.Body, func(n ast.Node) bool {
45 switch x := n.(type) {
46 case *ast.CallExpr:
47 if id, ok := x.Fun.(*ast.Ident); ok {
48 callers[id.Name] = append(callers[id.Name], fn.Name.Name)
49 }
50 case *ast.CompositeLit:
51 // The collector's registry listing: routes: r.Registry.List.
52 if typ, ok := x.Type.(*ast.Ident); ok && typ.Name == "rootMigrationCollector" {
53 for _, el := range x.Elts {
54 if kv, ok := el.(*ast.KeyValueExpr); ok {
55 if k, ok := kv.Key.(*ast.Ident); ok && k.Name == "routes" {
56 callers["collector.routes"] = append(callers["collector.routes"], fn.Name.Name)
57 }
58 }
59 }
60 }
61 }
62 return true
63 })
64 }
65 }
66 want := map[string][]string{
67 "claimMigrationSharedPaths": {"migrateRootClaimAuthority"},
68 "verifyMigrationWindow": {"migrateRootClaimAuthority", "verifyJournalMigrateWindow"},
69 "verifyJournalMigrateWindow": {"RunSystemJournalMigrate"},
70 "prepareRootFloorDirs": {"migrateRootClaimAuthority"},
71 "prepareRootFloorDirsOwned": {"prepareRootFloorDirs"},
72 "repairRootFloorDir": {"prepareRootFloorDirsOwned"},
73 "walkNoFollowDir": {"consoleDir", "repairRootFloorDir"},
74 "claimBoxDataCensus": {"claimDataCensus"},
75 "claimDataCensus": {"claimGlobalSharedPaths"},
76 "claimGlobalSharedPaths": {"OpenProductionClaimAssembly", "claimMigrationSharedPaths", "recoverRootInventoryStartup"},
77 "migrateRootClaimAuthority": {"migrateClaimJournalsWith"},
78 "prepareRootRetryUnitDirectory": {"installActivationControl", "readClaimUnitSettings"},
79 "collector.routes": {"migrateRootClaimAuthority"},
80 }
81 for name, w := range want {
82 got := slices.Compact(sortedCopy(callers[name]))
83 if !slices.Equal(got, w) {
84 t.Errorf("%s is called from %v, want %v", name, got, w)
85 }
86 }
87 checkBackfillDoneCallers(t)
88 checkRosterBindingCallers(t)
89}
90
91// Ruling 6 for roster binding (design-roster-revision-holds.md, task 10): a
92// claims-off or no-census box never builds a claim assembly or claim owners,
93// so it computes no binding, compares none, and settles nothing. The binding
94// is computed only by the claim roster adapter and the cleanup identity, both
95// built only by OpenClaimOwners, which only the claim assembly and the claim
96// journal migration call. SameBinding is called only at the four claim-mode
97// sites; the cold handover's fresh admission and obsolete settlement are
98// wired only into root's claim handler. The migration-state and floor seams,
99// the obsolete-intent settlement and the stop numeral bound keep their
100// callers. The whole module's non-test sources are scanned, so a new caller
101// anywhere fails here first.
102func checkRosterBindingCallers(t *testing.T) {
103 t.Helper()
104 callers := map[string][]string{}
105 fset := token.NewFileSet()
106 for _, root := range []string{"../..", "../../cmd", "../../internal"} {
107 err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
108 if err != nil {
109 return err
110 }
111 if d.IsDir() {
112 if path != root && (root == "../.." || strings.HasPrefix(d.Name(), ".") || d.Name() == "testdata" || d.Name() == "vendor") {
113 return filepath.SkipDir
114 }
115 return nil
116 }
117 if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
118 return nil
119 }
120 f, err := parser.ParseFile(fset, path, nil, parser.SkipObjectResolution)
121 if err != nil {
122 return err
123 }
124 rel, _ := filepath.Rel("../..", filepath.Dir(path))
125 for _, decl := range f.Decls {
126 fn, ok := decl.(*ast.FuncDecl)
127 if !ok || fn.Body == nil {
128 continue
129 }
130 name := rel + " " + fn.Name.Name
131 if fn.Recv != nil && len(fn.Recv.List) == 1 {
132 typ := fn.Recv.List[0].Type
133 if star, ok := typ.(*ast.StarExpr); ok {
134 typ = star.X
135 }
136 if id, ok := typ.(*ast.Ident); ok {
137 name = rel + " " + id.Name + "." + fn.Name.Name
138 }
139 }
140 ast.Inspect(fn.Body, func(n ast.Node) bool {
141 switch x := n.(type) {
142 case *ast.CallExpr:
143 callee := ""
144 switch f := x.Fun.(type) {
145 case *ast.Ident:
146 callee = f.Name
147 case *ast.SelectorExpr:
148 callee = f.Sel.Name
149 }
150 switch callee {
151 case "BoxBinding", "SameBinding", "verifyClaimCleanupEnrollment", "OpenClaimOwners", "SettleObsoleteColdHandover", "obsoleteIntent", "settleObsoleteIntent", "claimRootHTTP", "rootStopCoordinator",
152 "openRootMigrationState", "rootMigrationStartupFloors", "SettleObsoleteRootInventory", "boundRosterRevision":
153 callers[callee] = append(callers[callee], name)
154 }
155 case *ast.CompositeLit:
156 typ := ""
157 switch f := x.Type.(type) {
158 case *ast.Ident:
159 typ = f.Name
160 case *ast.SelectorExpr:
161 typ = f.Sel.Name
162 }
163 if typ == "productionClaimRoster" {
164 callers["productionClaimRoster{}"] = append(callers["productionClaimRoster{}"], name)
165 }
166 if typ == "ColdHandover" {
167 for _, el := range x.Elts {
168 if kv, ok := el.(*ast.KeyValueExpr); ok {
169 if k, ok := kv.Key.(*ast.Ident); ok && (k.Name == "Admit" || k.Name == "Obsolete") {
170 callers["ColdHandover{"+k.Name+"}"] = append(callers["ColdHandover{"+k.Name+"}"], name)
171 }
172 }
173 }
174 }
175 case *ast.AssignStmt:
176 for _, lhs := range x.Lhs {
177 if sel, ok := lhs.(*ast.SelectorExpr); ok && sel.Sel.Name == "Obsolete" {
178 callers["."+sel.Sel.Name+"="] = append(callers["."+sel.Sel.Name+"="], name)
179 }
180 // The seams stand in for root-owned state only in
181 // tests: a production reassignment would disable
182 // the migration directory read or the floor check.
183 if id, ok := lhs.(*ast.Ident); ok && (id.Name == "openRootMigrationState" || id.Name == "rootMigrationStartupFloors") {
184 callers[id.Name+"="] = append(callers[id.Name+"="], name)
185 }
186 }
187 }
188 return true
189 })
190 }
191 return nil
192 })
193 if err != nil {
194 t.Fatal(err)
195 }
196 }
197 want := map[string][]string{
198 "BoxBinding": {"internal/host productionClaimRoster.Actor", "internal/host verifyClaimCleanupEnrollment"},
199 "productionClaimRoster{}": {"internal/host ProductionClaimOwnerProvider.OpenClaimOwners"},
200 "verifyClaimCleanupEnrollment": {"internal/host ProductionClaimOwnerProvider.OpenClaimOwners"},
201 "OpenClaimOwners": {"internal/host OpenProductionClaimAssembly", "internal/host migrateClaimJournalsWith"},
202 "SameBinding": {"internal/host ClaimOwnerBindings.claimEvidenceCoordinator", "internal/host claimBoundIdentity", "internal/host claimRegionalEnrollment.VerifyRegional", "internal/host servingParkRoot.prepareOrCommitServingReboot"},
203 "ColdHandover{Admit}": {"internal/host ClaimRootHTTP.coldHandover"},
204 ".Obsolete=": {"internal/host ClaimRootHTTP.RecoverPending"},
205 "SettleObsoleteColdHandover": {"internal/hostbox ColdHandover.settleObsolete"},
206 "obsoleteIntent": {"internal/host ClaimRootHTTP.RecoverPending", "internal/host ClaimRootHTTP.recoverRootInventory"},
207 "settleObsoleteIntent": {"internal/host ClaimRootHTTP.recoverRootInventory"},
208 "claimRootHTTP": {"internal/host NewProductionRootClaimStop"},
209 "rootStopCoordinator": {"internal/host NewProductionRootClaimStop"},
210 // The root-owned migration directory's seam (census ruling (a),
211 // platform-d8) and fix round 1's floor seam: a third caller fails.
212 "openRootMigrationState": {"internal/host recoverRootInventoryStartup", "internal/host verifyRootMigrationStartup"},
213 "rootMigrationStartupFloors": {"internal/host verifyRootMigrationStartup"},
214 // Fix round 1: the obsolete-intent settlement and the numeral bound.
215 "SettleObsoleteRootInventory": {"internal/host ClaimRootHTTP.settleObsoleteIntent"},
216 "boundRosterRevision": {"internal/host ClaimStopCoordinator.FenceAndStop", "internal/host ClaimStopCoordinator.witnessNoProcess"},
217 }
218 for name, w := range want {
219 got := slices.Compact(sortedCopy(callers[name]))
220 if !slices.Equal(got, w) {
221 t.Errorf("%s is called from %v, want %v", name, got, w)
222 }
223 }
224 for _, seam := range []string{"openRootMigrationState=", "rootMigrationStartupFloors="} {
225 if got := callers[seam]; len(got) != 0 {
226 t.Errorf("%s is reassigned in production by %v, want nowhere", strings.TrimSuffix(seam, "="), got)
227 }
228 }
229 // Obsolete is set only by the assignment above, never in a literal. (An
230 // Admit assignment is not pinned: other types' Admit fields share the name.)
231 if got := callers["ColdHandover{Obsolete}"]; len(got) != 0 {
232 t.Errorf("ColdHandover{Obsolete} is set in %v, want nowhere", got)
233 }
234}
235
236// Ruling 6 for backfill-done (claim trial Option B): the new signer phase is
237// reached only through carlos system claims run on the trusted signer, and
238// its copy of the census's report check is reached only by Done.
239// The whole module's non-test sources are scanned, so a call from a box,
240// console or agent path fails here first. Backfill.Done is matched as a
241// three-argument Done call (ctx, signer, etag): context's Done takes none.
242func checkBackfillDoneCallers(t *testing.T) {
243 t.Helper()
244 callers := map[string][]string{}
245 fset := token.NewFileSet()
246 for _, root := range []string{"../..", "../../cmd", "../../internal"} {
247 err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
248 if err != nil {
249 return err
250 }
251 if d.IsDir() {
252 if path != root && (root == "../.." || strings.HasPrefix(d.Name(), ".") || d.Name() == "testdata" || d.Name() == "vendor") {
253 return filepath.SkipDir
254 }
255 return nil
256 }
257 if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
258 return nil
259 }
260 f, err := parser.ParseFile(fset, path, nil, parser.SkipObjectResolution)
261 if err != nil {
262 return err
263 }
264 rel, _ := filepath.Rel("../..", filepath.Dir(path))
265 for _, decl := range f.Decls {
266 fn, ok := decl.(*ast.FuncDecl)
267 if !ok || fn.Body == nil {
268 continue
269 }
270 name := rel + " " + fn.Name.Name
271 ast.Inspect(fn.Body, func(n ast.Node) bool {
272 call, ok := n.(*ast.CallExpr)
273 if !ok {
274 return true
275 }
276 switch f := call.Fun.(type) {
277 case *ast.Ident:
278 if f.Name == "verifyCensusReport" {
279 callers[f.Name] = append(callers[f.Name], name)
280 }
281 case *ast.SelectorExpr:
282 if f.Sel.Name == "backfillDone" || (f.Sel.Name == "Done" && len(call.Args) == 3) {
283 callers[f.Sel.Name] = append(callers[f.Sel.Name], name)
284 }
285 }
286 return true
287 })
288 }
289 return nil
290 })
291 if err != nil {
292 t.Fatal(err)
293 }
294 }
295 want := map[string][]string{
296 "Done": {"internal/claimactivation backfillDone"},
297 "backfillDone": {"internal/claimactivation Run"},
298 "verifyCensusReport": {"internal/hostops Done"},
299 }
300 for name, w := range want {
301 got := slices.Compact(sortedCopy(callers[name]))
302 if !slices.Equal(got, w) {
303 t.Errorf("%s is called from %v, want %v", name, got, w)
304 }
305 }
306}
307
308func sortedCopy(s []string) []string {
309 out := append([]string(nil), s...)
310 sort.Strings(out)
311 return out
312}
313