zdd plan 2 task 12: claimed spawns pin a generation child under the entry's parent
The spawnClaimedGen helper is built closed until main's isolated root launcher can carry claim reservations. On this branch, main (commit a4cd3003) deliberately closed the in-process claimed spawn path: spawnClaimedBefore returns ErrClaimIsolationUnavailable when enabled. The generation child shape must stay closed for the same reason. A later plan will route claims-on generation spawns through the isolated root launcher rather than reopen an in-process path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+49
−0
internal/activator/claimspawn.go+13 −0internal/activator/claimspawn_test.go+36 −0
diff --git a/internal/activator/claimspawn.go b/internal/activator/claimspawn.go| index 60b5cc0c8..578260f65 100644 |
| --- a/internal/activator/claimspawn.go |
| +++ b/internal/activator/claimspawn.go |
| @@ -21,3 +21,16 @@ func spawnClaimedBefore(ctx context.Context, enabled bool, manager *cgroup.Manag |
| // Keep this old helper closed even if a future caller reintroduces it. |
| return nil, nil, ErrClaimIsolationUnavailable |
| } |
| + |
| +// spawnClaimedGen pins and reserves a generation child under the entry's |
| +// parent for the generation layout under claims (zero-downtime spec section |
| +// 7.3). It stays closed until main's isolated root launcher can carry claim |
| +// reservations, like spawnClaimedBefore. A later plan must route claims-on |
| +// generation spawns through that launcher rather than reopen an in-process |
| +// path. |
| +func spawnClaimedGen(ctx context.Context, parent *cgroup.Prepared, n uint64, tier cgroup.Limit, selected hostwrite.Work, identity hostwrite.Identity, build func() *exec.Cmd, before func() error) (*exec.Cmd, *cgroup.Prepared, error) { |
| + if parent == nil || selected == nil || build == nil || n == 0 { |
| + return nil, nil, hostwrite.ErrUnavailable |
| + } |
| + return nil, nil, ErrClaimIsolationUnavailable |
| +} |
diff --git a/internal/activator/claimspawn_test.go b/internal/activator/claimspawn_test.go| index a6933ccca..5c5d45236 100644 |
| --- a/internal/activator/claimspawn_test.go |
| +++ b/internal/activator/claimspawn_test.go |
| @@ -39,3 +39,39 @@ func TestClaimSpawnNeverFallsBack(t *testing.T) { |
| t.Fatal(builds, calls) |
| } |
| } |
| + |
| +// spawnClaimedGen never falls back to an ordinary start: an invalid |
| +// argument refuses before the host write is entered, and a refused work or |
| +// a parent that cannot pin a child never builds the command. |
| +func TestClaimSpawnGenNeverFallsBack(t *testing.T) { |
| + calls, builds := 0, 0 |
| + beforeCalled := false |
| + build := func() *exec.Cmd { builds++; return exec.Command("must-not-execute") } |
| + before := func() error { beforeCalled = true; return nil } |
| + |
| + if _, _, err := spawnClaimedGen(context.Background(), nil, 1, cgroup.TierDefault, claimSpawnWork{false, &calls}, hostwrite.Identity{}, build, before); !errors.Is(err, hostwrite.ErrUnavailable) { |
| + t.Fatalf("nil parent: %v", err) |
| + } |
| + if _, _, err := spawnClaimedGen(context.Background(), &cgroup.Prepared{}, 0, cgroup.TierDefault, claimSpawnWork{false, &calls}, hostwrite.Identity{}, build, before); !errors.Is(err, hostwrite.ErrUnavailable) { |
| + t.Fatalf("generation 0: %v", err) |
| + } |
| + if calls != 0 { |
| + t.Fatalf("host write entered %d times for invalid arguments", calls) |
| + } |
| + for _, deny := range []bool{true, false} { |
| + beforeCalled = false |
| + cmd, child, err := spawnClaimedGen(context.Background(), &cgroup.Prepared{}, 7, cgroup.TierDefault, claimSpawnWork{deny, &calls}, hostwrite.Identity{}, build, before) |
| + if cmd != nil || child != nil { |
| + t.Fatalf("deny=%v: cmd=%v child=%v", deny, cmd, child) |
| + } |
| + if !errors.Is(err, ErrClaimIsolationUnavailable) { |
| + t.Fatalf("deny=%v: err=%v, want ErrClaimIsolationUnavailable", deny, err) |
| + } |
| + if beforeCalled { |
| + t.Fatalf("deny=%v: before was called", deny) |
| + } |
| + } |
| + if builds != 0 || calls != 0 { |
| + t.Fatalf("builds=%d calls=%d, want 0 and 0", builds, calls) |
| + } |
| +} |