carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package steering
2
3// deployment.go — this package's ONE exported converge entry point.
4//
5// Everything else here is unexported by design: desiredState is a pure fold
6// (steering.go) and converge takes a set that only that fold can build
7// (route53.go), so a caller outside this package cannot assemble the argument
8// converge needs. ConvergeDeployment is the seam that closes that gap for the
9// host housekeeper (internal/host/steering_loop.go) — it takes what a BOX has
10// (registry rows and raw edges-file bodies), folds, and converges, in one
11// call that cannot be told to converge a set the fold did not produce.
12//
13// Note on the client parameter's type. route53API stays unexported even
14// though this function is exported: every method in it is an exported SDK
15// method name, so any type implementing them satisfies it from any package —
16// Go checks method sets, not the interface's own visibility. The host caller
17// therefore needs nothing more than NewClient (route53.go), whose concrete
18// *route53.Client it can pass straight in, and no test outside this package
19// ever needs to fake AWS. Exporting the interface would add surface with no
20// consumer; the constructor is the seam, and it already exists.
21
22import (
23 "context"
24 "log"
25
26 "github.com/carlosframework/platform/internal/registry"
27)
28
29// ConvergeDeployment folds this deployment's routes and edges files into the
30// desired steering state and converges the zone to it.
31//
32// edgesFiles are the RAW bodies of control/edges/<box>.json, one per armed
33// box, in the caller's listing order (the host loader sorts by key, so the
34// order — and therefore the fold — is stable tick to tick).
35//
36// The two failure classes an edges file can have are handled in DIFFERENT
37// places on purpose, and the split is the durable half of Task 5's shrink-
38// guard finding:
39//
40// - a file that could not be READ (the listing failed, or a Get failed) is
41// a VISIBILITY problem: the deployment cannot see all of its edges, so a
42// fold built from what came back is SHORT, and a short fold deletes the
43// records of every box that went missing. That case must never reach this
44// function — the caller fails closed and returns the error instead (see
45// internal/host/steering_loop.go's loadEdgesFileBodies).
46// - a file that was read but does not PARSE is a CONTENT problem, and it is
47// handled HERE by skipping it and saying so. A single box publishing a
48// corrupt or future-shaped body must not wedge the whole deployment's
49// steering, and the skip is bounded: the box simply contributes no
50// records this pass, which is the same as being unarmed.
51//
52// The distinction is not cosmetic. Both end in "one fewer edge in the fold",
53// but only the read failure means the deployment does not KNOW what it has —
54// and converge's own guards (the empty-set refusal and the proportional
55// shrink limit) are the last line, not the first. This is the first.
56func ConvergeDeployment(ctx context.Context, client route53API, zoneID string, routes []registry.Route, edgesFiles [][]byte, appsDomain string) error {
57 edges := make([]edgesFileView, 0, len(edgesFiles))
58 for i, body := range edgesFiles {
59 f, err := parseEdgesFileView(body)
60 if err != nil {
61 // The bodies arrive without their keys (the caller hands over
62 // []byte by design, so this package never touches a store), so
63 // the position is the handle an operator gets: bodies are passed
64 // in the caller's sorted listing order, making "file 2 of 3" the
65 // 2nd key under control/edges/. The length disambiguates an empty
66 // object from a truncated one.
67 log.Printf("steering: skipping edges file %d of %d (%d bytes): %v — that box contributes no records this pass", i+1, len(edgesFiles), len(body), err)
68 continue
69 }
70 edges = append(edges, f)
71 }
72 return converge(ctx, client, zoneID, desiredState(routes, edges, appsDomain))
73}
74