| index ad5243aa8..fc61916d4 100644 |
| --- a/internal/hostops/executor_test.go |
| +++ b/internal/hostops/executor_test.go |
| @@ -20,6 +20,13 @@ import ( |
| ) |
| |
| func chooserFixture(t *testing.T, boxes ...string) (ExecutorChooser, release.Store, *boxchoice.Snapshot, map[string]boxchoice.Load) { |
| + t.Helper() |
| + return chooserFixtureWith(t, nil, boxes...) |
| +} |
| + |
| +// chooserFixtureWith is chooserFixture with edit applied to the roster |
| +// before it is signed (another namespace or pool for some box). |
| +func chooserFixtureWith(t *testing.T, edit func(*hostactors.Roster), boxes ...string) (ExecutorChooser, release.Store, *boxchoice.Snapshot, map[string]boxchoice.Load) { |
| t.Helper() |
| s := release.NewDirStore(t.TempDir()) |
| pub, key, err := ed25519.GenerateKey(rand.Reader) |
| @@ -37,6 +44,9 @@ func chooserFixture(t *testing.T, boxes ...string) (ExecutorChooser, release.Sto |
| snap.Boxes = append(snap.Boxes, boxchoice.Box{BoxID: ref, EIP: "203.0.113.1", Region: "eu-west-1", Placement: "ie", Fresh: true, |
| HibernationConfigured: &yes, ServesSharedPool: &yes, AcceptsInbound: &yes, SecretsKeyFP: "fp", Capabilities: []string{boxchoice.Capability}}) |
| } |
| + if edit != nil { |
| + edit(&roster) |
| + } |
| wire, err := release.SignRootEnvelope(release.RootSigner(key), roster) |
| if err != nil { |
| t.Fatal(err) |
| @@ -45,7 +55,7 @@ func chooserFixture(t *testing.T, boxes ...string) (ExecutorChooser, release.Sto |
| t.Fatal(err) |
| } |
| loads := map[string]boxchoice.Load{} |
| - x := ExecutorChooser{Directory: hostactors.Directory{Store: s, PublicKey: pub, Bucket: "bucket"}, SecretsKeyFP: "fp", |
| + x := ExecutorChooser{Directory: hostactors.Directory{Store: s, PublicKey: pub, Bucket: "bucket"}, SecretsKeyFP: "fp", Namespace: "platform", |
| Edges: func(context.Context) (boxchoice.Snapshot, error) { return *snap, nil }, |
| Loads: func(context.Context) (map[string]boxchoice.Load, error) { return loads, nil }, |
| Now: func() time.Time { return time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) }} |
| @@ -53,7 +63,8 @@ func chooserFixture(t *testing.T, boxes ...string) (ExecutorChooser, release.Sto |
| } |
| |
| func chooserRecord() Record { |
| - return Record{Holder: hostclaim.NewHolder{Family: "instance", Account: "bab", App: "jam", Host: "shop.bab.oncarlos.com", Region: "ie"}} |
| + return Record{Holder: hostclaim.NewHolder{Family: "instance", Account: "bab", App: "jam", Host: "shop.bab.oncarlos.com", Region: "ie"}, |
| + Body: []byte(`{"host":"shop.bab.oncarlos.com","account":"bab","app":"jam","region":"ie"}`)} |
| } |
| |
| func TestExecutorChooserSpreadsThenBalancesThenHashes(t *testing.T) { |
| @@ -255,12 +266,12 @@ func TestProductionAuthoritySelectsOnlyWithAChooser(t *testing.T) { |
| x, _, snap, _ := chooserFixture(t, "box-a", "box-b") |
| snap.Boxes[0].Draining = true |
| absent := hostclaim.Observed{Host: "shop.bab.oncarlos.com", Absent: true} |
| - sel, selected, err = (ProductionAuthority{Executors: &x}).SelectExecutor(context.Background(), absent, chooserRecord()) |
| + sel, selected, err = (ProductionAuthority{Executors: &x, Namespace: "platform"}).SelectExecutor(context.Background(), absent, chooserRecord()) |
| if err != nil || !selected || sel.Box != "box-b" || sel.Enrollment != strings.Repeat("b", 32) { |
| t.Fatalf("with a chooser: %+v %v %v", sel, selected, err) |
| } |
| snap.Boxes[1].Draining = true |
| - if sel, selected, err = (ProductionAuthority{Executors: &x}).SelectExecutor(context.Background(), absent, chooserRecord()); !errors.Is(err, ErrNoEligibleBox) || selected { |
| + if sel, selected, err = (ProductionAuthority{Executors: &x, Namespace: "platform"}).SelectExecutor(context.Background(), absent, chooserRecord()); !errors.Is(err, ErrNoEligibleBox) || selected { |
| t.Fatalf("no eligible box: %+v %v %v", sel, selected, err) |
| } |
| } |
| @@ -274,7 +285,6 @@ func TestProductionCreateSelectsThroughTheChooser(t *testing.T) { |
| ctx := context.Background() |
| x, s, snap, _ := chooserFixture(t, "box-a", "box-b") |
| r := chooserRecord() |
| - r.Body = []byte(`{"host":"shop.bab.oncarlos.com","account":"bab","app":"jam","region":"ie"}`) |
| absent := hostclaim.Observed{Host: r.Holder.Host, Absent: true} |
| a := ProductionAuthority{Store: s, Directory: x.Directory, Namespace: "platform", Now: x.Now} |
| off, _, pool, err := a.selectExecutor(ctx, absent, r) |
| @@ -295,8 +305,8 @@ func TestProductionCreateSelectsThroughTheChooser(t *testing.T) { |
| if err != nil || box != other || enrollment != strings.Repeat(map[string]string{"box-a": "a", "box-b": "b"}[other], 32) || pool != "default" { |
| t.Fatalf("with a chooser: %s %s %s %v, want %s", box, enrollment, pool, err, other) |
| } |
| - // Another console namespace: the chooser still names a box, but not |
| - // one Plan 5's scope holds. |
| + // Another console namespace: the chooser chooses within it, and it |
| + // holds no box. |
| a.Namespace = "elsewhere" |
| if box, _, _, err := a.selectExecutor(ctx, absent, r); !errors.Is(err, ErrNoEligibleBox) { |
| t.Fatalf("a box outside the namespace: %s %v", box, err) |
| @@ -308,3 +318,79 @@ func TestProductionCreateSelectsThroughTheChooser(t *testing.T) { |
| t.Fatalf("no eligible box: %s %v", box, err) |
| } |
| } |
| + |
| +// Fix round 1 (review Important 1): the chooser chooses within claim Plan |
| +// 5's scope, so a region that also holds another namespace's or another |
| +// pool's boxes places on the in-scope eligible box, never refusing because |
| +// an out-of-scope box would have won. |
| +func TestProductionCreateWithAChooserPlacesWithinPlan5sScope(t *testing.T) { |
| + ctx := context.Background() |
| + absent := hostclaim.Observed{Host: "shop.bab.oncarlos.com", Absent: true} |
| + // The console's chooser carries no scope of its own: the authority |
| + // supplies its namespace and the record's pool. |
| + authority := func(x *ExecutorChooser, s release.Store) ProductionAuthority { |
| + x.Namespace, x.Pool = "", "" |
| + return ProductionAuthority{Store: s, Directory: x.Directory, Namespace: "platform", Now: x.Now, Executors: x} |
| + } |
| + t.Run("another namespace's box would win on load", func(t *testing.T) { |
| + x, s, _, loads := chooserFixtureWith(t, func(r *hostactors.Roster) { |
| + b := r.Boxes["box-b"] |
| + b.Namespace = "other" |
| + r.Boxes["box-b"] = b |
| + }, "box-a", "box-b") |
| + loads["box-a"] = boxchoice.Load{Hosts: 5, Apps: map[string]int{"bab/jam": 1}} |
| + box, enrollment, pool, err := authority(&x, s).selectExecutor(ctx, absent, chooserRecord()) |
| + if err != nil || box != "box-a" || enrollment != strings.Repeat("a", 32) || pool != "default" { |
| + t.Fatalf("got %s %s %s %v, want the in-scope box-a", box, enrollment, pool, err) |
| + } |
| + }) |
| + t.Run("a named pool", func(t *testing.T) { |
| + // box-b serves only pool acme: its edges file does not serve the |
| + // shared pool. box-a is the shared pool's, and idle. |
| + x, s, snap, loads := chooserFixtureWith(t, func(r *hostactors.Roster) { |
| + b := r.Boxes["box-b"] |
| + b.Pool = "acme" |
| + r.Boxes["box-b"] = b |
| + }, "box-a", "box-b") |
| + no := false |
| + snap.Boxes[1].ServesSharedPool = &no |
| + loads["box-b"] = boxchoice.Load{Hosts: 5} |
| + named := chooserRecord() |
| + named.Body = []byte(`{"host":"shop.bab.oncarlos.com","account":"bab","app":"jam","region":"ie","pool":"acme"}`) |
| + box, enrollment, pool, err := authority(&x, s).selectExecutor(ctx, absent, named) |
| + if err != nil || box != "box-b" || enrollment != strings.Repeat("b", 32) || pool != "acme" { |
| + t.Fatalf("named pool: got %s %s %s %v, want box-b in acme", box, enrollment, pool, err) |
| + } |
| + // The shared pool still places on its own box, whatever the load. |
| + loads["box-a"] = boxchoice.Load{Hosts: 50} |
| + if box, _, pool, err := authority(&x, s).selectExecutor(ctx, absent, chooserRecord()); err != nil || box != "box-a" || pool != "default" { |
| + t.Fatalf("shared pool: got %s %s %v, want box-a", box, pool, err) |
| + } |
| + // The named pool's box must still be eligible on its own evidence. |
| + snap.Boxes[1].Draining = true |
| + if box, _, _, err := authority(&x, s).selectExecutor(ctx, absent, named); !errors.Is(err, ErrNoEligibleBox) { |
| + t.Fatalf("a draining named-pool box: %s %v", box, err) |
| + } |
| + }) |
| + t.Run("an unreadable fence on another namespace's box", func(t *testing.T) { |
| + x, s, _, _ := chooserFixtureWith(t, func(r *hostactors.Roster) { |
| + b := r.Boxes["box-b"] |
| + b.Namespace = "other" |
| + r.Boxes["box-b"] = b |
| + }, "box-a", "box-b") |
| + if _, err := s.Put(ctx, "control/box-fences/"+strings.Repeat("b", 32)+".json", []byte("{"), ""); err != nil { |
| + t.Fatal(err) |
| + } |
| + if box, _, _, err := authority(&x, s).selectExecutor(ctx, absent, chooserRecord()); err != nil || box != "box-a" { |
| + t.Fatalf("got %s %v, want box-a: an out-of-scope fence never blocks", box, err) |
| + } |
| + // In scope, the same unreadable fence still refuses (absence is |
| + // never evidence). |
| + if _, err := s.Put(ctx, "control/box-fences/"+strings.Repeat("a", 32)+".json", []byte("{"), ""); err != nil { |
| + t.Fatal(err) |
| + } |
| + if _, _, _, err := authority(&x, s).selectExecutor(ctx, absent, chooserRecord()); err == nil || errors.Is(err, ErrNoEligibleBox) { |
| + t.Fatalf("an unreadable in-scope fence must refuse: %v", err) |
| + } |
| + }) |
| +} |