carlos / platform Public

console: eligible boxes, one at a time, and a choice that spreads an app

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 2ca53cd3c9d96403824bdac13a0b6535285f5184 parent 5447e3f
2 files changed, +261 −0
  • internal/console/boxchoice.go +98 −0
  • internal/console/boxchoice_test.go +163 −0
diff --git a/internal/console/boxchoice.go b/internal/console/boxchoice.go
new file mode 100644
index 000000000..35f81de3b
--- /dev/null
+++ b/internal/console/boxchoice.go
@@ -0,0 +1,98 @@
+package console
+
+import (
+ "slices"
+ "sort"
+
+ "github.com/carlosframework/platform/internal/boxid"
+)
+
+// boxCandidate is one box that may be named as a host's executor (region HA
+// spec 1 §2, "eligible box").
+type boxCandidate struct {
+ BoxID string
+ Region string
+ MemFreeMiB, DiskFreeMiB int64
+ Awake int
+}
+
+// eligibleBoxes is regionCandidates' predicate applied to one box at a time,
+// plus what a multi-box region adds: a verified box record that agrees with
+// the file's box_id and whose signed region is awsRegion (the AWS region
+// the caller expects for this placement), the HA capability, not draining,
+// not lost. Two boxes
+// that each satisfy half of it never add up to one.
+//
+// An incomplete snapshot offers nothing: an unread file could be the one
+// box that would change the answer.
+func eligibleBoxes(snap edgesSnapshot, verified map[string]boxid.Identity, placement, awsRegion, homeName, expectedFP string, lost map[string]bool) []boxCandidate {
+ if !snap.complete() || expectedFP == "" || awsRegion == "" {
+ return nil
+ }
+ var out []boxCandidate
+ for _, f := range snap.Files {
+ code := f.Placement
+ if code == "" || (homeName != "" && code == homeName) {
+ code = ""
+ }
+ want := placement
+ if homeName != "" && want == homeName {
+ want = ""
+ }
+ id, ok := verified[f.BoxID]
+ switch {
+ case code != want:
+ case !f.Fresh, f.EIP == "", f.BoxID == "":
+ case !ok || id.InstanceID == "":
+ case id.Region != awsRegion, f.Region != awsRegion:
+ // The placement code is the box's own word. The AWS region is
+ // signed: a box whose identity is in another region is not in
+ // this placement, whatever its file says.
+ case !isTrue(f.HibernationConfigured), !isTrue(f.ServesSharedPool), !isTrue(f.AcceptsInbound):
+ case f.SecretsKeyFP != expectedFP:
+ case !slices.Contains(f.Capabilities, "region-ha-1"):
+ case f.Draining, lost[f.BoxID]:
+ default:
+ c := boxCandidate{BoxID: f.BoxID, Region: id.Region}
+ if f.Headroom != nil {
+ c.MemFreeMiB, c.DiskFreeMiB, c.Awake = f.Headroom.MemFreeMiB, f.Headroom.DiskFreeMiB, f.Headroom.Awake
+ }
+ out = append(out, c)
+ }
+ }
+ sort.Slice(out, func(i, j int) bool { return out[i].BoxID < out[j].BoxID })
+ return out
+}
+
+// boxLoad is what claims already place on a box.
+type boxLoad struct {
+ Hosts int
+ Apps map[string]int // "acct/app" -> hosts of that app
+}
+
+// chooseBox is spec 1 §5.2's interim rule until spec 2: prefer a box that
+// runs no instance of this app (soft spread: it never refuses), then the
+// fewest hosts overall, then the lowest box id.
+func chooseBox(cands []boxCandidate, running map[string]boxLoad, app string) (boxCandidate, bool) {
+ if len(cands) == 0 {
+ return boxCandidate{}, false
+ }
+ best := cands[0]
+ for _, c := range cands[1:] {
+ cl, bl := running[c.BoxID], running[best.BoxID]
+ cRuns, bRuns := cl.Apps[app] > 0, bl.Apps[app] > 0
+ switch {
+ case cRuns != bRuns:
+ if !cRuns {
+ best = c
+ }
+ case cl.Hosts != bl.Hosts:
+ if cl.Hosts < bl.Hosts {
+ best = c
+ }
+ case c.BoxID < best.BoxID:
+ best = c
+ }
+ }
+ return best, true
+}
diff --git a/internal/console/boxchoice_test.go b/internal/console/boxchoice_test.go
new file mode 100644
index 000000000..18713ecc6
--- /dev/null
+++ b/internal/console/boxchoice_test.go
@@ -0,0 +1,163 @@
+package console
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/boxid"
+)
+
+// haBox is an eligible box's edges file in placement "uk".
+func haBox(id string, mut func(m map[string]any)) string {
+ m := map[string]any{
+ "v": 1, "box": id, "box_id": id, "eip": "1.2.3.4", "region": "eu-west-2", "placement": "uk",
+ "hosts": map[string]any{}, "hibernation_configured": true, "serves_shared_pool": true,
+ "accepts_inbound": true, "secrets_key_fp": testKeyFP, "capabilities": []string{"region-ha-1"},
+ "headroom": map[string]any{"mem_free_mib": 1000, "disk_free_mib": 1000, "awake": 1},
+ }
+ if mut != nil {
+ mut(m)
+ }
+ b, _ := json.Marshal(m)
+ return string(b)
+}
+
+func ids(cs []boxCandidate) []string {
+ out := make([]string, len(cs))
+ for i, c := range cs {
+ out[i] = c.BoxID
+ }
+ return out
+}
+
+func verifiedFor(boxes ...string) map[string]boxid.Identity {
+ out := map[string]boxid.Identity{}
+ for _, b := range boxes {
+ out[b] = boxid.Identity{AccountID: "1", Region: "eu-west-2", InstanceID: b}
+ }
+ return out
+}
+
+func TestEligibleBoxesAppliesThePredicateOneBoxAtATime(t *testing.T) {
+ del := func(k string) func(map[string]any) { return func(m map[string]any) { delete(m, k) } }
+ set := func(k string, v any) func(map[string]any) { return func(m map[string]any) { m[k] = v } }
+ cases := []struct {
+ name string
+ mut func(map[string]any)
+ want bool
+ }{
+ {"all present", nil, true},
+ {"no dial address", set("eip", ""), false},
+ {"hibernation unconfigured", set("hibernation_configured", false), false},
+ {"hibernation unknown", del("hibernation_configured"), false},
+ {"refuses the shared pool", set("serves_shared_pool", false), false},
+ {"refuses inbound", set("accepts_inbound", false), false},
+ {"wrong secrets key", set("secrets_key_fp", "sha256:other"), false},
+ {"no capability", del("capabilities"), false},
+ {"draining", set("draining", true), false},
+ {"another placement", set("placement", "us"), false},
+ {"box_id disagrees with its record", set("box_id", "i-0zzz"), false},
+ }
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ snap := views(t, haBox("i-0aaa", tc.mut))
+ got := eligibleBoxes(snap, verifiedFor("i-0aaa"), "uk", "eu-west-2", "ie", testKeyFP, nil)
+ if (len(got) == 1) != tc.want {
+ t.Fatalf("eligible = %v, want %v", ids(got), tc.want)
+ }
+ })
+ }
+}
+
+func TestEligibleBoxesNeedsAVerifiedRecordFreshnessAndNotLost(t *testing.T) {
+ snap := views(t, haBox("i-0aaa", nil), haBox("i-0bbb", nil), haBox("i-0ccc", nil))
+ snap = markStale(snap, "i-0bbb")
+ got := eligibleBoxes(snap, verifiedFor("i-0aaa", "i-0bbb"), "uk", "eu-west-2", "ie", testKeyFP, nil)
+ if len(got) != 1 || got[0].BoxID != "i-0aaa" {
+ t.Fatalf("got %v, want only i-0aaa (i-0bbb is stale, i-0ccc has no verified record)", ids(got))
+ }
+ got = eligibleBoxes(snap, verifiedFor("i-0aaa"), "uk", "eu-west-2", "ie", testKeyFP, map[string]bool{"i-0aaa": true})
+ if len(got) != 0 {
+ t.Fatalf("a lost box is eligible: %v", ids(got))
+ }
+}
+
+// TestEligibilityNeverCombinesBoxes is nearest-region's rule for boxes: one
+// box holding the key and another accepting inbound do not add up to one.
+func TestEligibleBoxesNeverCombinesTwoHalfBoxes(t *testing.T) {
+ snap := views(t,
+ haBox("i-0aaa", func(m map[string]any) { m["secrets_key_fp"] = "" }),
+ haBox("i-0bbb", func(m map[string]any) { m["accepts_inbound"] = false }))
+ if got := eligibleBoxes(snap, verifiedFor("i-0aaa", "i-0bbb"), "uk", "eu-west-2", "ie", testKeyFP, nil); len(got) != 0 {
+ t.Fatalf("got %v, want none", ids(got))
+ }
+}
+
+func TestEligibleBoxesAnIncompleteSnapshotOffersNothing(t *testing.T) {
+ snap := views(t, haBox("i-0aaa", nil))
+ snap.Complete = false
+ if got := eligibleBoxes(snap, verifiedFor("i-0aaa"), "uk", "eu-west-2", "ie", testKeyFP, nil); len(got) != 0 {
+ t.Fatalf("got %v from an incomplete snapshot", ids(got))
+ }
+}
+
+func TestEligibleBoxesHomePlacementMatchesTheEmptyCode(t *testing.T) {
+ home := haBox("i-0aaa", func(m map[string]any) { m["placement"] = "" })
+ named := haBox("i-0bbb", func(m map[string]any) { m["placement"] = "ie" })
+ snap := views(t, home, named)
+ got := eligibleBoxes(snap, verifiedFor("i-0aaa", "i-0bbb"), "", "eu-west-2", "ie", testKeyFP, nil)
+ if len(got) != 2 {
+ t.Fatalf("got %v, want both home boxes", ids(got))
+ }
+}
+
+func TestChooseBox(t *testing.T) {
+ a, b, c := boxCandidate{BoxID: "i-0aaa"}, boxCandidate{BoxID: "i-0bbb"}, boxCandidate{BoxID: "i-0ccc"}
+ load := func(hosts int, apps ...string) boxLoad {
+ l := boxLoad{Hosts: hosts, Apps: map[string]int{}}
+ for _, x := range apps {
+ l.Apps[x]++
+ }
+ return l
+ }
+ if _, ok := chooseBox(nil, nil, "bab/jam"); ok {
+ t.Fatal("chose from nothing")
+ }
+ got, _ := chooseBox([]boxCandidate{a, b}, map[string]boxLoad{"i-0aaa": load(5), "i-0bbb": load(2)}, "bab/jam")
+ if got.BoxID != "i-0bbb" {
+ t.Fatalf("fewest hosts: got %s", got.BoxID)
+ }
+ got, _ = chooseBox([]boxCandidate{a, b}, map[string]boxLoad{"i-0aaa": load(5), "i-0bbb": load(2, "bab/jam")}, "bab/jam")
+ if got.BoxID != "i-0aaa" {
+ t.Fatalf("spread beats load: got %s", got.BoxID)
+ }
+ got, ok := chooseBox([]boxCandidate{a, b}, map[string]boxLoad{"i-0aaa": load(5, "bab/jam"), "i-0bbb": load(2, "bab/jam", "bab/jam")}, "bab/jam")
+ if !ok || got.BoxID != "i-0bbb" {
+ t.Fatalf("every box runs the app: fewest hosts overall, never a refusal; got %s ok=%v", got.BoxID, ok)
+ }
+ got, _ = chooseBox([]boxCandidate{c, b, a}, nil, "bab/jam")
+ if got.BoxID != "i-0aaa" {
+ t.Fatalf("a tie breaks by box id: got %s", got.BoxID)
+ }
+}
+
+// A box whose SIGNED region is not the placement's AWS region is not in
+// that placement, even when its own file says it is and both regions have
+// certificates.
+func TestEligibleBoxesRefusesABoxSignedInAnotherRegion(t *testing.T) {
+ snap := views(t, haBox("i-0aaa", nil), haBox("i-0bbb", nil))
+ verified := verifiedFor("i-0aaa", "i-0bbb")
+ verified["i-0bbb"] = boxid.Identity{AccountID: "1", Region: "us-east-1", InstanceID: "i-0bbb"}
+ got := eligibleBoxes(snap, verified, "uk", "eu-west-2", "ie", testKeyFP, nil)
+ if len(got) != 1 || got[0].BoxID != "i-0aaa" {
+ t.Fatalf("got %v, want only i-0aaa", ids(got))
+ }
+ // And a file that advertises another AWS region than the one expected.
+ snap = views(t, haBox("i-0aaa", func(m map[string]any) { m["region"] = "us-east-1" }))
+ if got := eligibleBoxes(snap, verifiedFor("i-0aaa"), "uk", "eu-west-2", "ie", testKeyFP, nil); len(got) != 0 {
+ t.Fatalf("got %v, want none", ids(got))
+ }
+ if got := eligibleBoxes(views(t, haBox("i-0aaa", nil)), verifiedFor("i-0aaa"), "uk", "", "ie", testKeyFP, nil); len(got) != 0 {
+ t.Fatalf("got %v with no expected region", ids(got))
+ }
+}