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
3import (
4 "bytes"
5 "log"
6 "reflect"
7 "strings"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/registry"
11)
12
13func captureLog(t *testing.T) *bytes.Buffer {
14 t.Helper()
15 var buf bytes.Buffer
16 prev := log.Writer()
17 flags := log.Flags()
18 log.SetOutput(&buf)
19 log.SetFlags(0)
20 t.Cleanup(func() { log.SetOutput(prev); log.SetFlags(flags) })
21 return &buf
22}
23
24func steeredRoute(host string) registry.Route {
25 return registry.Route{Host: host, Steering: "latency"}
26}
27
28// TestDesiredStateReplicasDoNotSteer pins the #162 half of the fold: an
29// account whose replica box publishes the host marked replica gets records
30// from EVERY armed edge EXCEPT that one — the pre-#162 rule is any-edge-
31// any-app (far edges backhaul), so the ONLY entries a replica mark removes
32// are its own. The synthetic edge.<apps-domain> records keep ALL armed
33// edges regardless (every edge is a cache front).
34func TestDesiredStateReplicasDoNotSteer(t *testing.T) {
35 dublin := edgeHomes("dublin", "1.2.3.4", "eu-west-1", "app.example.com")
36 oregon := edge("oregon", "5.6.7.8", "us-west-2")
37 oregon.Hosts = map[string]edgeHostView{
38 "app.example.com": {Replica: true},
39 }
40
41 got := desiredState([]registry.Route{steeredRoute("app.example.com")},
42 []edgesFileView{dublin, oregon}, "example.com")
43
44 want := []desiredRecord{
45 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
46 {Name: "edge.example.com.", EIP: "5.6.7.8", Box: "oregon", Region: "us-west-2"},
47 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
48 }
49 if !reflect.DeepEqual(got, want) {
50 t.Fatalf("desiredState() =\n%#v\nwant\n%#v", got, want)
51 }
52
53 // A host named by NO box still earns records from every armed edge:
54 // steering is any-edge-any-app, and the far edge backhauls to the home
55 // whether or not this box holds a row for the host.
56 got = desiredState([]registry.Route{steeredRoute("never-published.example.com")},
57 []edgesFileView{dublin, oregon}, "example.com")
58 want = []desiredRecord{
59 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
60 {Name: "edge.example.com.", EIP: "5.6.7.8", Box: "oregon", Region: "us-west-2"},
61 {Name: "never-published.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
62 {Name: "never-published.example.com.", EIP: "5.6.7.8", Box: "oregon", Region: "us-west-2"},
63 }
64 if !reflect.DeepEqual(got, want) {
65 t.Fatalf("unpublished-host desiredState() =\n%#v\nwant\n%#v", got, want)
66 }
67}
68
69func edge(box, eip, region string) edgesFileView {
70 return edgesFileView{V: 1, Box: box, EIP: eip, Region: region}
71}
72
73// edgeHomes is edge() with the homed-host set filled in: the tests that
74// drive desiredState's per-host fold need Hosts, which pre-#162 was
75// deliberately absent (the fold never asked who served what) and which the
76// replica-aware fold now reads. Hosts uses the writer's own key shape;
77// every entry is home-marked (Replica clear) unless a test says otherwise.
78func edgeHomes(box, eip, region string, homes ...string) edgesFileView {
79 e := edge(box, eip, region)
80 e.Hosts = map[string]edgeHostView{}
81 for _, h := range homes {
82 e.Hosts[h] = edgeHostView{}
83 }
84 return e
85}
86
87// TestDesiredState is the brief's table: one steered host x two armed
88// edges = two records; unsteered host absent; edge.<apps-domain> present
89// whenever >=1 edge is armed, absent at zero.
90func TestDesiredState(t *testing.T) {
91 dublin := edgeHomes("dublin", "1.2.3.4", "eu-west-1",
92 "app.example.com", "steered.example.com", "example.com")
93 oregon := edgeHomes("oregon", "5.6.7.8", "us-west-2",
94 "app.example.com", "steered.example.com")
95
96 tests := []struct {
97 name string
98 routes []registry.Route
99 edges []edgesFileView
100 appsDomain string
101 want []desiredRecord
102 }{
103 {
104 name: "one steered host x two armed edges",
105 routes: []registry.Route{steeredRoute("app.example.com")},
106 edges: []edgesFileView{dublin, oregon},
107 appsDomain: "example.com",
108 want: []desiredRecord{
109 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
110 {Name: "edge.example.com.", EIP: "5.6.7.8", Box: "oregon", Region: "us-west-2"},
111 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
112 {Name: "app.example.com.", EIP: "5.6.7.8", Box: "oregon", Region: "us-west-2"},
113 },
114 },
115 {
116 name: "unsteered host absent",
117 routes: []registry.Route{
118 {Host: "app.example.com", Steering: ""},
119 steeredRoute("steered.example.com"),
120 },
121 edges: []edgesFileView{dublin},
122 appsDomain: "example.com",
123 want: []desiredRecord{
124 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
125 {Name: "steered.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
126 },
127 },
128 {
129 name: "zero armed edges yields nothing, no edge.<apps-domain>",
130 routes: []registry.Route{steeredRoute("app.example.com")},
131 edges: nil,
132 appsDomain: "example.com",
133 want: nil,
134 },
135 {
136 name: "edge with empty EIP skipped",
137 routes: []registry.Route{steeredRoute("app.example.com")},
138 edges: []edgesFileView{dublin, edge("noeip", "", "us-west-2")},
139 appsDomain: "example.com",
140 want: []desiredRecord{
141 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
142 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
143 },
144 },
145 {
146 name: "edge with empty region skipped",
147 routes: []registry.Route{steeredRoute("app.example.com")},
148 edges: []edgesFileView{dublin, edge("noregion", "9.9.9.9", "")},
149 appsDomain: "example.com",
150 want: []desiredRecord{
151 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
152 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
153 },
154 },
155 {
156 name: "edge region carrying a placement code (not an AWS region) skipped",
157 routes: []registry.Route{steeredRoute("app.example.com")},
158 edges: []edgesFileView{dublin, edge("tito", "9.9.9.9", "ie")},
159 appsDomain: "example.com",
160 want: []desiredRecord{
161 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
162 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
163 },
164 },
165 {
166 name: "pool-carrying steered route excluded",
167 routes: []registry.Route{
168 {Host: "pooled.example.com", Steering: "latency", Pool: "reserved"},
169 },
170 edges: []edgesFileView{dublin},
171 appsDomain: "example.com",
172 want: []desiredRecord{
173 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
174 },
175 },
176 {
177 name: "wildcard registry row with steering set never produces a record",
178 routes: []registry.Route{
179 {Host: "*.example.com", Steering: "latency"},
180 },
181 edges: []edgesFileView{dublin},
182 appsDomain: "example.com",
183 want: []desiredRecord{
184 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
185 },
186 },
187 {
188 name: "invalid steering value skipped, never guessed",
189 routes: []registry.Route{
190 {Host: "hand-written.example.com", Steering: "geo"},
191 },
192 edges: []edgesFileView{dublin},
193 appsDomain: "example.com",
194 want: []desiredRecord{
195 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
196 },
197 },
198 {
199 name: "trailing-dot input normalizes to a single trailing dot",
200 routes: []registry.Route{steeredRoute("app.example.com.")},
201 edges: []edgesFileView{dublin},
202 appsDomain: "example.com.",
203 want: []desiredRecord{
204 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
205 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
206 },
207 },
208 {
209 name: "mixed-case host folds to one lowercase Name",
210 routes: []registry.Route{steeredRoute("App.Example.COM")},
211 edges: []edgesFileView{dublin},
212 appsDomain: "example.com",
213 want: []desiredRecord{
214 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
215 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
216 },
217 },
218 {
219 name: "blank apps domain skips the synthetic edge hostname AND every per-host record",
220 routes: []registry.Route{steeredRoute("app.example.com")},
221 edges: []edgesFileView{dublin},
222 appsDomain: "",
223 want: nil,
224 },
225 {
226 name: "out-of-zone steered host excluded, in-zone host still lands",
227 routes: []registry.Route{
228 steeredRoute("app.example.com"),
229 steeredRoute("console.carloku.com"),
230 },
231 edges: []edgesFileView{dublin},
232 appsDomain: "example.com",
233 want: []desiredRecord{
234 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
235 {Name: "app.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
236 },
237 },
238 {
239 name: "steered host equal to the apps domain itself (apex) lands",
240 routes: []registry.Route{steeredRoute("example.com")},
241 edges: []edgesFileView{dublin},
242 appsDomain: "example.com",
243 want: []desiredRecord{
244 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
245 {Name: "example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
246 },
247 },
248 {
249 name: "steered host merely sharing a suffix (not a subdomain) is excluded",
250 routes: []registry.Route{steeredRoute("notexample.com")},
251 edges: []edgesFileView{dublin},
252 appsDomain: "example.com",
253 want: []desiredRecord{
254 {Name: "edge.example.com.", EIP: "1.2.3.4", Box: "dublin", Region: "eu-west-1"},
255 },
256 },
257 }
258
259 for _, tt := range tests {
260 t.Run(tt.name, func(t *testing.T) {
261 captureLog(t) // silence + isolate log output from test noise
262 got := desiredState(tt.routes, tt.edges, tt.appsDomain)
263 if !reflect.DeepEqual(got, tt.want) {
264 t.Fatalf("desiredState() =\n%#v\nwant\n%#v", got, tt.want)
265 }
266 })
267 }
268}
269
270func TestDesiredStateLogsEachSkipReason(t *testing.T) {
271 dublin := edge("dublin", "1.2.3.4", "eu-west-1")
272
273 tests := []struct {
274 name string
275 routes []registry.Route
276 edges []edgesFileView
277 wantLog string
278 }{
279 {
280 name: "edge with no EIP is named in the log",
281 edges: []edgesFileView{edge("noeip", "", "eu-west-1")},
282 wantLog: "steering: edge noeip unarmed: no EIP",
283 },
284 {
285 name: "edge with a placement code names the bad value",
286 edges: []edgesFileView{edge("tito", "9.9.9.9", "ie")},
287 wantLog: `steering: edge tito unarmed: region "ie" is not an AWS region`,
288 },
289 {
290 name: "pool-carrying route is named",
291 edges: []edgesFileView{dublin},
292 routes: []registry.Route{{Host: "pooled.example.com", Steering: "latency", Pool: "reserved"}},
293 wantLog: `steering: route pooled.example.com excluded: pool "reserved" set`,
294 },
295 {
296 name: "wildcard route is named",
297 edges: []edgesFileView{dublin},
298 routes: []registry.Route{{Host: "*.example.com", Steering: "latency"}},
299 wantLog: "steering: route *.example.com excluded: wildcard host",
300 },
301 {
302 name: "invalid steering value is named verbatim",
303 edges: []edgesFileView{dublin},
304 routes: []registry.Route{{Host: "hand.example.com", Steering: "geo"}},
305 wantLog: `steering: route hand.example.com excluded: invalid steering value "geo"`,
306 },
307 {
308 name: "out-of-zone steered host is named, with the CNAME guidance",
309 edges: []edgesFileView{dublin},
310 routes: []registry.Route{steeredRoute("console.carloku.com")},
311 wantLog: `steering: route console.carloku.com excluded: host is not under the apps domain "example.com" — custom domains steer via CNAME to edge.<apps-domain>`,
312 },
313 }
314
315 for _, tt := range tests {
316 t.Run(tt.name, func(t *testing.T) {
317 buf := captureLog(t)
318 desiredState(tt.routes, tt.edges, "example.com")
319 if !strings.Contains(buf.String(), tt.wantLog) {
320 t.Fatalf("log output =\n%s\nwant substring %q", buf.String(), tt.wantLog)
321 }
322 })
323 }
324}
325
326// TestDesiredStateBlankAppsDomainExcludesEveryPerHostRecord pins the second
327// half of the Important review finding: appsDomain == "" is not just "no
328// synthetic edge.<apps-domain> record" — with no domain there is no zone a
329// per-host route can be proven to lie under, so EVERY steered route is
330// excluded too (steeredHosts' own appsDomain == "" branch), loudly, one
331// line per route, same log family as the out-of-zone case.
332func TestDesiredStateBlankAppsDomainExcludesEveryPerHostRecord(t *testing.T) {
333 dublin := edge("dublin", "1.2.3.4", "eu-west-1")
334 buf := captureLog(t)
335
336 got := desiredState(
337 []registry.Route{steeredRoute("app.example.com"), steeredRoute("other.example.com")},
338 []edgesFileView{dublin},
339 "",
340 )
341 if got != nil {
342 t.Fatalf("desiredState() = %#v, want nil (no domain, no synthetic hostname, no per-host record)", got)
343 }
344
345 logged := buf.String()
346 for _, want := range []string{
347 `steering: route app.example.com excluded: no apps domain configured`,
348 `steering: route other.example.com excluded: no apps domain configured`,
349 "custom domains steer via CNAME to edge.<apps-domain>",
350 } {
351 if !strings.Contains(logged, want) {
352 t.Fatalf("log output =\n%s\nwant substring %q", logged, want)
353 }
354 }
355}
356
357// TestDesiredStateCaseFoldDedupe pins the case-collision half of the
358// review's Important finding: two registry rows differing only by case
359// are two legitimate primary-key rows right up until fqdn folds them onto
360// one Route53 name, at which point desiredState must produce exactly ONE
361// record per armed edge for that name, not two — and the log must name
362// BOTH original (still mixed-case) Host values, not the already-folded
363// Name, because that is the only information an operator can act on:
364// which two registry rows to go fix. That naming has to happen where the
365// originals still exist — steeredHosts, via dedupeCaseCollisions — not
366// later at the desiredRecord level, where a same-edge collision has
367// identical Box/EIP on both sides and the log would just repeat the one
368// folded Name.
369func TestDesiredStateCaseFoldDedupe(t *testing.T) {
370 dublin := edgeHomes("dublin", "1.2.3.4", "eu-west-1", "app.example.com")
371 buf := captureLog(t)
372
373 routes := []registry.Route{
374 steeredRoute("App.example.com"),
375 steeredRoute("app.example.com"),
376 }
377 got := desiredState(routes, []edgesFileView{dublin}, "example.com")
378
379 var matches int
380 for _, rec := range got {
381 if rec.Name == "app.example.com." {
382 matches++
383 }
384 }
385 if matches != 1 {
386 t.Fatalf("desiredState() produced %d records for the case-colliding name, want exactly 1 (got %#v)", matches, got)
387 }
388 // Total should be the edge.<apps-domain> record plus the ONE surviving
389 // per-host record — not two.
390 if len(got) != 2 {
391 t.Fatalf("desiredState() = %#v, want 2 records (1 synthetic + 1 deduped)", got)
392 }
393
394 logged := buf.String()
395 // Sorted-Host order (ASCII, uppercase < lowercase) keeps
396 // "App.example.com" and drops "app.example.com" — both original
397 // strings, plus the folded DNS name, must appear verbatim.
398 for _, want := range []string{`"App.example.com"`, `"app.example.com"`, `"app.example.com."`} {
399 if !strings.Contains(logged, want) {
400 t.Fatalf("log output =\n%s\nwant substring %s naming the original colliding host/DNS name", logged, want)
401 }
402 }
403 if !strings.Contains(logged, "steering: case-collision:") {
404 t.Fatalf("log output =\n%s\nwant a case-collision line", logged)
405 }
406 // The record-level backstop (dedupeRecords) must NOT be the one that
407 // fired — the collision was already resolved at the route level, one
408 // layer earlier, where the log could still name both original hosts.
409 if strings.Contains(logged, "steering: record ") {
410 t.Fatalf("log output =\n%s\nwant no dedupeRecords line — the collision should be caught earlier, in steeredHosts", logged)
411 }
412}
413
414func TestDesiredStateBlankAppsDomainLogged(t *testing.T) {
415 dublin := edge("dublin", "1.2.3.4", "eu-west-1")
416 buf := captureLog(t)
417 desiredState([]registry.Route{steeredRoute("app.example.com")}, []edgesFileView{dublin}, "")
418 want := "steering: no apps domain configured"
419 if !strings.Contains(buf.String(), want) {
420 t.Fatalf("log output =\n%s\nwant substring %q", buf.String(), want)
421 }
422}
423
424func TestParseEdgesFileView(t *testing.T) {
425 tests := []struct {
426 name string
427 body string
428 want edgesFileView
429 wantErr bool
430 }{
431 {
432 name: "v1 body with region",
433 body: `{"v":1,"box":"dublin","eip":"1.2.3.4","region":"eu-west-1","generation":"2026-08-22T09:00:00Z","hosts":{"app.example.com":{"role":"home"}}}`,
434 want: edgesFileView{V: 1, Box: "dublin", EIP: "1.2.3.4", Region: "eu-west-1"},
435 },
436 {
437 name: "v1 body without region (pre-region writer)",
438 body: `{"v":1,"box":"dublin","eip":"1.2.3.4","generation":"2026-08-22T09:00:00Z","hosts":{}}`,
439 want: edgesFileView{V: 1, Box: "dublin", EIP: "1.2.3.4", Region: ""},
440 },
441 {
442 name: "unversioned body refused",
443 body: `{"box":"dublin","eip":"1.2.3.4"}`,
444 wantErr: true,
445 },
446 {
447 name: "garbage body refused",
448 body: `not json`,
449 wantErr: true,
450 },
451 }
452
453 for _, tt := range tests {
454 t.Run(tt.name, func(t *testing.T) {
455 got, err := parseEdgesFileView([]byte(tt.body))
456 if tt.wantErr {
457 if err == nil {
458 t.Fatalf("parseEdgesFileView(%q) = %+v, nil; want error", tt.body, got)
459 }
460 return
461 }
462 if err != nil {
463 t.Fatalf("parseEdgesFileView(%q) unexpected error: %v", tt.body, err)
464 }
465 // Field-by-field, not struct equality: Hosts is a map, and a
466 // comparable-struct check died the day #162 added it. The
467 // scalar fields are the ones this test pins; Hosts is read
468 // only for the replica mark in desiredState, pinned there.
469 if got.V != tt.want.V || got.Box != tt.want.Box || got.EIP != tt.want.EIP ||
470 got.Region != tt.want.Region {
471 t.Fatalf("parseEdgesFileView(%q) = %+v, want %+v", tt.body, got, tt.want)
472 }
473 })
474 }
475}
476
477func TestArmedEdgesSortedByBox(t *testing.T) {
478 edges := []edgesFileView{
479 edge("oregon", "5.6.7.8", "us-west-2"),
480 edge("dublin", "1.2.3.4", "eu-west-1"),
481 }
482 got := armedEdges(edges)
483 if len(got) != 2 || got[0].Box != "dublin" || got[1].Box != "oregon" {
484 t.Fatalf("armedEdges() = %+v, want dublin then oregon", got)
485 }
486}
487