carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "slices"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/boxchoice"
10 "github.com/carlosframework/platform/internal/hostactors"
11 "github.com/carlosframework/platform/internal/hostclaim"
12 "github.com/carlosframework/platform/internal/release"
13)
14
15// The console's chooser reads the bucket through its two production
16// readers: the live edges (each box's edges file joined with its status
17// snapshot) and the serving claims' loads. The bucket holds real evidence,
18// the assertions name boxes and counts, and a change to the stored
19// evidence shows in the next read, so neither reader can be replaced by
20// one returning nothing (round 10).
21func TestCreateExecutorChooserReadsTheLiveEdgesAndTheClaims(t *testing.T) {
22 ctx := context.Background()
23 store := release.NewDirStore(t.TempDir())
24 off := &Server{cfg: Config{Releases: store, HomeRegion: "ie"}}
25 if off.executorChooser(hostactors.Directory{Store: store, Bucket: "bucket"}) != nil {
26 t.Fatal("a chooser with CARLOS_REGION_HA off: create would change")
27 }
28 s := &Server{cfg: Config{Releases: store, HomeRegion: "ie", RegionHA: true}}
29 x := s.executorChooser(hostactors.Directory{Store: store, Bucket: "bucket"})
30 if x == nil || x.Edges == nil || x.Loads == nil || x.Now == nil || x.HomeName != "ie" || x.SecretsKeyFP != s.expectedSecretsFP() {
31 t.Fatalf("chooser: %+v", x)
32 }
33
34 // Two live boxes in ie, each with its edges file and a fresh status
35 // snapshot; box-b advertises the region-ha-1 capability.
36 edges := func(box, extra string) string {
37 return `{"v":1,"box":"` + box + `","box_id":"` + box + `","eip":"203.0.113.1","region":"eu-west-1","placement":"ie","hibernation_configured":true` + extra + `,"hosts":{}}`
38 }
39 for box, body := range map[string]string{"box-a": edges("box-a", ""), "box-b": edges("box-b", `,"capabilities":["`+boxchoice.Capability+`"]`)} {
40 setBoxSnapshot(t, store, box, time.Now())
41 if _, err := store.Put(ctx, "control/edges/"+box+".json", []byte(body), ""); err != nil {
42 t.Fatal(err)
43 }
44 }
45 // Two serving claims on box-a for bab/jam, one on box-b for bab/tea.
46 created := time.Date(2026, 10, 4, 1, 0, 0, 0, time.UTC)
47 serve := func(host, app, box string) {
48 t.Helper()
49 nh := hostclaim.NewHolder{Family: "instance", Key: "apps/bab/" + app + "/instances/" + host + ".json", Account: "bab", App: app, Host: host, Region: "ie", RecordRegion: "ie", CreatedAt: created}
50 body := []byte(`{"host":"` + host + `","account":"bab","app":"` + app + `","region":"ie","created_at":"2026-10-04T01:00:00Z"}`)
51 o, err := hostclaim.Read(ctx, store, host)
52 if err != nil {
53 t.Fatal(err)
54 }
55 if o, err = hostclaim.Update(ctx, store, o, hostclaim.BeginCreate(nh, body, "c", "console", created)); err != nil {
56 t.Fatal(err)
57 }
58 if _, err = hostclaim.Update(ctx, store, o, hostclaim.CompleteCreate("c", box)); err != nil {
59 t.Fatal(err)
60 }
61 }
62 serve("a.bab.oncarlos.com", "jam", "box-a")
63 serve("b.bab.oncarlos.com", "jam", "box-a")
64 serve("c.bab.oncarlos.com", "tea", "box-b")
65
66 read := func() (map[string]boxchoice.Box, map[string]boxchoice.Load) {
67 t.Helper()
68 snap, err := x.Edges(ctx)
69 if err != nil || !snap.Complete {
70 t.Fatalf("edges: %+v %v", snap, err)
71 }
72 boxes := map[string]boxchoice.Box{}
73 for _, b := range snap.Boxes {
74 boxes[b.BoxID] = b
75 }
76 loads, err := x.Loads(ctx)
77 if err != nil {
78 t.Fatal(err)
79 }
80 return boxes, loads
81 }
82 boxes, loads := read()
83 if len(boxes) != 2 || !boxes["box-a"].Fresh || !boxes["box-b"].Fresh || boxes["box-a"].Placement != "ie" ||
84 slices.Contains(boxes["box-a"].Capabilities, boxchoice.Capability) || !slices.Contains(boxes["box-b"].Capabilities, boxchoice.Capability) {
85 t.Fatalf("live edges: %+v", boxes)
86 }
87 if loads["box-a"].Hosts != 2 || loads["box-a"].Apps["bab/jam"] != 2 || loads["box-b"].Hosts != 1 || loads["box-b"].Apps["bab/tea"] != 1 {
88 t.Fatalf("loads: %+v", loads)
89 }
90
91 // The evidence changes: box-a starts draining and stops reporting
92 // (stale), and a fourth host serves on box-b. The next read says so.
93 _, etag, err := store.Get(ctx, "control/edges/box-a.json")
94 if err != nil {
95 t.Fatal(err)
96 }
97 if _, err := store.Put(ctx, "control/edges/box-a.json", []byte(edges("box-a", `,"draining":true`)), etag); err != nil {
98 t.Fatal(err)
99 }
100 setBoxSnapshot(t, store, "box-a", time.Now().Add(-time.Hour))
101 serve("d.bab.oncarlos.com", "tea", "box-b")
102 boxes, loads = read()
103 if !boxes["box-a"].Draining || boxes["box-a"].Fresh || !boxes["box-b"].Fresh {
104 t.Fatalf("live edges after the change: %+v", boxes)
105 }
106 if loads["box-b"].Hosts != 2 || loads["box-b"].Apps["bab/tea"] != 2 || loads["box-a"].Hosts != 2 {
107 t.Fatalf("loads after the change: %+v", loads)
108 }
109}
110