Skip to content

feat(orchestrator): add NetworkAssignHook for sandbox lifecycle extensions#3290

Merged
rguliyev merged 6 commits into
mainfrom
startup-hook-pr
Jul 15, 2026
Merged

feat(orchestrator): add NetworkAssignHook for sandbox lifecycle extensions#3290
rguliyev merged 6 commits into
mainfrom
startup-hook-pr

Conversation

@rguliyev

@rguliyev rguliyev commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Sandbox editions sometimes need bounded, local prep after a sandbox's identity/network slot are assigned but before its guest runs - a gap between the two existing extension points: EgressProxy fires before identity exists, MapSubscriber's OnInsert fires only after the guest is already running.

Add NetworkAssignHook (OnNetworkAssign), called once per start attempt right after AssignNetwork in CreateSandbox/ResumeSandbox, tagged with a NetworkAssignReason since these carry different guest-state guarantees. Called synchronously with the caller's own ctx and no derived timeout: the implementation is responsible for bounding its own duration. A panic is recovered, since there is no other recovery layer between this call and the process's main goroutine. Can never fail a sandbox start; a returned error is logged and swallowed.

No-op everywhere by default; zero behavior change until an edition wires one in.

Benchmarked on the no-op path: ~10ns/op, 0 B/op, 0 allocs/op - noise next to a sandbox create/resume (ms-s).

Sandbox editions sometimes need bounded, local prep after a sandbox's
identity/network slot are assigned but before its guest runs - a gap
between the two existing extension points: EgressProxy fires before
identity exists, MapSubscriber's OnInsert fires only after the guest
is already running.

Add StartHook (BeforeStart), called once per start attempt right after
AssignNetwork in CreateSandbox/ResumeSandbox, tagged with a
StartReason (create/resume/reboot/throwaway_resume) since these carry
different guest-state guarantees. Bounded and non-fatal by
construction: runs in its own goroutine behind a timeout enforced via
select, not by trusting the implementation to honor ctx; a panic is
recovered. Can never delay or fail a sandbox start.

No-op everywhere by default; zero behavior change until an edition
wires one in.

Benchmarked on the no-op path: low single-digit µs/op, 576 B/op, 7
allocs/op - noise next to a sandbox create/resume (ms-s).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a StartHook interface and a StartReason enum to allow edition-specific hooks to run before a sandbox starts (during creation, resume, reboot, or throwaway resume). The hook is integrated into the sandbox Factory and executed asynchronously with a 2-second timeout. The review feedback highlights opportunities to improve logging and error handling: specifically, suppressing redundant context cancellation/timeout warnings in the hook execution, distinguishing between parent context cancellation and hook timeouts, and implementing the fmt.Stringer interface for StartReason to prevent raw numeric logging.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/orchestrator/pkg/sandbox/sandbox.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/sandbox.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/start_hook.go Outdated
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
355 5 350 1
View the top 3 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory
Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir
Stack Traces | 0.84s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
--- FAIL: TestListDir (0.84s)
View the full list of 1 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files

Flake rate in main: 30.77% (Passed 9 times, Failed 4 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@rguliyev rguliyev requested a review from wj-e2b July 15, 2026 00:20
- suppress redundant "hook failed" logging when BeforeStart returns
  context.Canceled/DeadlineExceeded - already logged by the
  timeout/cancellation branch
- distinguish parent-context cancellation from startHookTimeout
  actually elapsing in that branch's log message
- add StartReason.String() so logging it prints a name, not a bare
  uint8

Adds coverage for all three.
@rguliyev rguliyev requested a review from tvi July 15, 2026 00:34
Comment thread packages/orchestrator/pkg/sandbox/start_hook.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/start_hook.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/sandbox.go Outdated
rguliyev and others added 3 commits July 15, 2026 06:31
…imeout

Renames StartHook/BeforeStart/StartReason to NetworkAssignHook/
OnNetworkAssign/NetworkAssignReason, matching the OnInsert/
OnNetworkRelease naming convention (per PR review feedback).

Also drops infra's own timeout enforcement on the hook call: it's now
invoked synchronously with the caller's own ctx, unmodified, and no
derived deadline. The implementation is fully responsible for
constraining its own duration; infra only recovers a panic here, since
there's no other recovery layer before the process's main goroutine.
golangci-lint's fatcontext linter flags any context.Context value
assigned to an outer-scoped variable from within a function literal,
even when nothing is actually chained via context.WithValue. Assert
the context identity inline instead of capturing it, same test intent.
@rguliyev rguliyev changed the title feat(orchestrator): add pre-start hook for sandbox lifecycle extensions feat(orchestrator): add NetworkAssignHook for sandbox lifecycle extensions Jul 15, 2026
@rguliyev rguliyev merged commit 3261963 into main Jul 15, 2026
40 checks passed
@rguliyev rguliyev deleted the startup-hook-pr branch July 15, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants