carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1// Package edgesfile READS the control/edges/<box>.json wire shape — the
2// per-box publication of what a box homes (multi-region ingress spec, Part
3// 3) — for consumers that need the deployment-wide picture rather than one
4// box's own view.
5//
6// The WRITER's shape lives in internal/host (edgesFile/edgeHost): the box
7// agent owns what it publishes, and this package deliberately does not try
8// to own it too. internal/host cannot host this reader — the console does
9// not import internal/host, and internal/host imports internal/edge and
10// never the reverse — so the reader lives one level down where the console,
11// internal/domains and the box agent can all reach it.
12//
13// The two shapes are joined by a test, not by a shared type: keeping them
14// separate lets the writer add a field and roll before every reader
15// understands it, which is the whole point of an additive wire.
16//
17// Two readers deliberately keep their own narrow decoders and are NOT
18// migrated: internal/edge/homemap.go (the serving hot path, with its own
19// polling and staleness machinery) and internal/steering (which reads no
20// role values — since #162 it does read the per-host replica mark, but its
21// decode stays its own for the same isolation reason). Both are named here
22// so the consumer list stays complete rather than merely short.
23package edgesfile
24
25import (
26 "encoding/json"
27 "fmt"
28)
29
30// Prefix is the flat prefix every box's file lives under.
31const Prefix = "control/edges/"
32
33// RoleHome is the role a box publishes for a host it actually serves. An
34// entry with any other role is either a cert-only role (see Host.Wildcard)
35// or a role a newer build publishes; readers must ignore what they do not
36// recognise rather than route on it or fail the parse.
37const RoleHome = "home"
38
39// Key is the bucket key for box's file.
40func Key(box string) string { return Prefix + box + ".json" }
41
42// Host is one per-host entry, decoded. Every mark is additive, and each
43// defaults to false on a file written before that mark existed — but they
44// fail in OPPOSITE directions, which is deliberate and load-bearing:
45//
46// - Platform absent reads as "not platform", so a pre-mark publisher's rows
47// derive no wildcard parent (#300 fails CLOSED).
48// - Alias absent reads as "not an alias", so a pre-mark publisher's rows
49// keep full route authority and cannot be outranked by a claimed alias
50// (#287 fails OPEN, which is why its readers must roll first).
51//
52// The writer always emits "platform" and omits the other two when false;
53// this reader does not care which, because absent and false decode alike.
54// What the reader must never do is re-serialize a Host and call it a
55// publication — that is the writer's job, and round-tripping through this
56// struct would drop any field a newer box publishes.
57type Host struct {
58 Role string `json:"role"`
59 Platform bool `json:"platform"`
60 Alias bool `json:"alias"`
61 // Wildcard marks a delegated customer wildcard parent (wildcard_domains,
62 // #305): the key is "*.<parent>". With Role RoleHome the box also serves
63 // the catch-all; with any other role it is the cert half alone.
64 Wildcard bool `json:"wildcard"`
65}
66
67// File is one box's published file.
68type File struct {
69 V int `json:"v"`
70 Box string `json:"box"`
71 EIP string `json:"eip"`
72 Region string `json:"region,omitempty"`
73 Generation string `json:"generation,omitempty"`
74 Hosts map[string]Host `json:"hosts"`
75}
76
77// Parse decodes one box's body. Pure — no store I/O. V < 1 (no version field
78// at all) is refused as not a file this reader understands; a HIGHER version
79// is accepted, because the schema grows additively and a newer box's file
80// still carries box/eip/hosts verbatim.
81func Parse(body []byte) (File, error) {
82 var f File
83 if err := json.Unmarshal(body, &f); err != nil {
84 return File{}, fmt.Errorf("edgesfile: parse: %w", err)
85 }
86 if f.V < 1 {
87 return File{}, fmt.Errorf("edgesfile: unversioned edges file")
88 }
89 return f, nil
90}
91