Skip to content

refactor(orchestrator): Fix leaking resources on shutdown and add framework for better lifecycle tracking - #2770

Closed
wj-e2b wants to merge 6 commits into
mainfrom
infra-orchestrator
Closed

wj-e2b wants to merge 6 commits into
mainfrom
infra-orchestrator

Conversation

@wj-e2b

@wj-e2b wj-e2b commented May 21, 2026 •

Copy link
Copy Markdown
Contributor

Pass 1.

Do some mild lifecycle tracking and then fix all the shutdown cleanup bugs.
Verified by shutting down orchestrator and seeing no leaked kernel resources.
Should enable graceful shutdowns.

@cursor

cursor Bot commented May 21, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Changes core shutdown, sandbox/network/Firecracker teardown, and drain semantics on production orchestrator nodes; mistakes could strand VMs, leak netns/iptables, or block deploys.

Overview
This PR adds orchestrator drain/shutdown orchestration so nodes can stop without leaving kernel/network resources behind. Nomad’s orchestrator task kill_timeout is set to 24h to align with long graceful drains.

Lifecycle tracking extends the sandbox map with a lifecycle index (MarkStopped, WaitLifecycles) so shutdown can wait until cleanup finishes even after sandboxes leave the live set. The factory and gRPC/template servers enter drain mode, reject new starts, and wait for in-flight create/build operations via start gates.

Shutdown flow in Run now drains the orchestrator server, template manager, then either gracefully drains sandboxes or force-stops them (with fallback if drain times out) before closing other services.

Cleanup fixes: Firecracker stop signals the whole process group and polls until it is gone; network teardown ignores already-removed iptables/routes/links/namespaces and rolls back partial setup on create failure; nftables firewall tables are deleted on close; network slot return is synchronous; the network pool cleans up slots created while closing.

Minor shutdown noise handling treats expected serviceDoneError and EINVAL on logger Sync as non-fatal.

Reviewed by Cursor Bugbot for commit 3c8efb1. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented May 21, 2026 •

Copy link
Copy Markdown

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
2748 4 2744 7
View the full list of 4 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSandboxListPaginationRunningLargerLimit

Flake rate in main: 40.08% (Passed 885 times, Failed 592 times)

Stack Traces | 91.3s run time
=== RUN   TestSandboxListPaginationRunningLargerLimit
    sandbox_list_test.go:327: Created sandbox 1/12: i6xa3spd5o13iuvz99dw6
    sandbox_list_test.go:327: Created sandbox 2/12: iljo5n4x0w1wch9ntv1m6
    sandbox_list_test.go:327: Created sandbox 3/12: i38xh3qmwy87jwjkmxjw1
    sandbox_list_test.go:327: Created sandbox 4/12: ij68n908q810n9ad1ixn7
    sandbox_list_test.go:327: Created sandbox 5/12: iltockn22wmlhc3a6n4ci
    sandbox_list_test.go:327: Created sandbox 6/12: iq6azmg0dio5fn1cw7dup
    sandbox_list_test.go:327: Created sandbox 7/12: ir3w9eazprvjakssqlcph
    sandbox_list_test.go:327: Created sandbox 8/12: ijwiggzdz15kyrvkypml1
    sandbox_list_test.go:327: Created sandbox 9/12: ifvw4cnpacmayqd7uv1ni
    sandbox_list_test.go:327: Created sandbox 10/12: itqshi0l3vry6nxrqnhw7
    sandbox_list_test.go:327: Created sandbox 11/12: isoxhf3ejdhmi2t9cs6hk
    sandbox_list_test.go:327: Created sandbox 12/12: i5f66uk4hpisgtsds1oz0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:340
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	"[]" should have 12 item(s), but has 0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:330
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxListPaginationRunningLargerLimit
--- FAIL: TestSandboxListPaginationRunningLargerLimit (91.34s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 54.88% (Passed 878 times, Failed 1068 times)

Stack Traces | 73.2s run time
=== RUN   TestSandboxMemoryIntegrity
=== PAUSE TestSandboxMemoryIntegrity
=== CONT  TestSandboxMemoryIntegrity
    sandbox_memory_integrity_test.go:27: Build completed successfully
--- FAIL: TestSandboxMemoryIntegrity (73.18s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity/tmpfs_hash

Flake rate in main: 54.96% (Passed 868 times, Failed 1059 times)

Stack Traces | 204s run time
=== RUN   TestSandboxMemoryIntegrity/tmpfs_hash
=== PAUSE TestSandboxMemoryIntegrity/tmpfs_hash
=== CONT  TestSandboxMemoryIntegrity/tmpfs_hash
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{start:{pid:1251}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Total memory: 985 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory before tmpfs mount: 191 MB\nFree memory before tmpfs mount: 793 MB\nMemory to use in integrity test (60% of free, min 64MB): 475 MB\n"}}
Executing command bash in sandbox i2mv12qdfifjg35o363gh (user: root)
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"475+0 records in\n475+0 records out\n498073600 bytes (498 MB, 475 MiB) copied, 3.32929 s, 150 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\tCommand being timed: \"dd if=/dev/urandom of=/mnt/testfile bs=1M count=475\"\n\tUser time (seconds): 0.00\n\tSystem time (seconds): 3.26\n\tPercent of CPU this job got: 97%\n\tElapsed (wall clock) time (h:mm:ss or m:ss): 0:03.33\n\tAverage shared text size (kbytes): 0\n\tAverage unshared data size (kbytes): 0\n\tAverage stack size (kbytes): 0\n\tAverage total size (kbytes): 0\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Maximum resident set s"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ize (kbyt"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"es): 2652\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Average resident set size (kbytes): 0\n\tMajor (requiring I/O) page faults: 3\n\tMinor (reclaiming a frame) page f"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"aults: 344\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\tVolun"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"tary c"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ontext"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" swit"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ches: "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"4\n\tIn"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"volunt"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ary c"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ontext"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" swit"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ches: "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"15\n\tSw"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"aps: "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n\tFil"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"e sys"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"tem inputs: 176\n\tFile system outputs: 0\n\tSocket messages sent: 0\n\tSock"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"et messag"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"es rec"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"eived"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:": 0\n\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"Signal"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"s del"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ivere"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"d: 0\n\tPage "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"size "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"(bytes"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"): 4096"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tExi"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"t sta"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"tus: "}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"0\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory after tmpfs mount and file fill: 673 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] completed successfully in sandbox ibzgixi3oveag1g7w1woe
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1268}}
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{data:{stdout:"1cad0f3d410a2d3a1a264f44c4f6168d022cd426ac6ac899980aec226ca9235a\n"}}
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:80: Command [bash] completed successfully in sandbox ibzgixi3oveag1g7w1woe
Executing command bash in sandbox izvhx7mkfbcawhoj15a5q (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1271}}
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
Executing command bash in sandbox ibzgixi3oveag1g7w1woe (user: root)
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:81
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	Received unexpected error:
        	            	failed to execute command bash in sandbox ibzgixi3oveag1g7w1woe: unavailable: HTTP status 502 Bad Gateway
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:78
        	            				.../tests/orchestrator/sandbox_memory_integrity_test.go:110
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxMemoryIntegrity/tmpfs_hash
--- FAIL: TestSandboxMemoryIntegrity/tmpfs_hash (204.08s)
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestEnvdAccessTokenAutoResumeViaProxy

Flake rate in main: 39.97% (Passed 874 times, Failed 582 times)

Stack Traces | 10.7s run time
=== RUN   TestEnvdAccessTokenAutoResumeViaProxy
=== PAUSE TestEnvdAccessTokenAutoResumeViaProxy
=== CONT  TestEnvdAccessTokenAutoResumeViaProxy
Executing command ls in sandbox iutpk1rk9s47dkmqvebpm
    traffic_access_token_test.go:357: 
        	Error Trace:	.../tests/proxies/traffic_access_token_test.go:357
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002/health": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestEnvdAccessTokenAutoResumeViaProxy
--- FAIL: TestEnvdAccessTokenAutoResumeViaProxy (10.72s)

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

@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

The use of wg.Go in ForceStopSandboxes will cause a compilation error because sync.WaitGroup does not provide that method. In doStop, returning early when WaitWithContext fails prevents memory resource cleanup, which can lead to leaked userfaultfd processes.

Comment thread packages/orchestrator/pkg/server/main.go Outdated
Comment thread packages/orchestrator/pkg/sandbox/sandbox.go
Comment thread packages/orchestrator/pkg/server/main.go Outdated
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 7324b01 to 8c5b901 Compare May 21, 2026 02:25
Comment thread packages/orchestrator/pkg/sandbox/fc/process.go Outdated
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 8c5b901 to 6443f22 Compare May 21, 2026 23:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6443f22c3b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/server/main.go
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/lifecycle/manager.go Outdated
Comment thread packages/orchestrator/pkg/lifecycle/manager.go Outdated
Comment thread packages/orchestrator/pkg/lifecycle/manager.go Outdated
live *smap.Map[*Sandbox]
network *smap.Map[*Sandbox]
live *smap.Map[*Sandbox]
lifecycles *smap.Map[lifecycleEntry]

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.

Is this a superset of the live and network maps? Can we remove either of those and exclusively use this new map?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

They're all used for different purposes that are disjoint. We can maybe merge the live one at some point, but this requires a bunch of code changes

Comment thread packages/orchestrator/pkg/server/main.go Outdated
Comment thread packages/orchestrator/pkg/server/main.go Outdated
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
@djeebus

djeebus commented May 22, 2026

Copy link
Copy Markdown
Contributor

Before merging, we should do the following:

  • update traffic simulator to add some slow sandboxes (even just sleep $(( (RANDOM % 1800) + 1 )) would probably work for this purpose)
  • deploy this branch to somewhere that can be hit by traffic simulator (not sure how to deploy to a dev cluster, but that would work)
  • randomly restart one orchestrator nomad allocation every ~15 minutes
  • wait 24 hours
  • ensure that every sandbox ran correctly

That should give us enough confidence to say "this is fixed"

@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 6443f22 to 31f1f51 Compare June 2, 2026 00:09
Comment thread packages/orchestrator/pkg/sandbox/map_test.go
Comment thread packages/orchestrator/pkg/factories/run.go Outdated
Comment thread packages/orchestrator/pkg/server/utils.go
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from a07b91d to 4ef20ff Compare June 2, 2026 01:06
@wj-e2b

wj-e2b commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

@claude review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ef20ffa5c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/factories/run.go Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is a substantial shutdown/lifecycle refactor touching critical orchestrator paths — the inline finding on the lifecycle map race is worth addressing before merge, and the outstanding architectural threads and your own traffic-simulator soak plan make this a clear human-review case.

Extended reasoning...

Overview

This PR refactors orchestrator shutdown across 13 files: introduces a new sandbox-lifecycle index (Map.lifecycles) with running/stopping states, adds a graceful DrainSandboxes / ForceStopSandboxes path with an in-flight start gate (sandboxStartMu), changes Firecracker teardown to signal the whole process group, hardens iptables/route/link/namespace removal against benign ENOENT/ESRCH/EPERM, removes the nftables firewall table on close, rolls back partial network setup on create failure, and converts the network-slot return from a detached goroutine to synchronous cleanup. Also wires the sandbox drain closer into the shutdown closer chain and ignores benign EINVAL from logger Sync() and the expected serviceDoneError.

Security risks

No direct authn/authz/crypto changes. The closest security-relevant change is signalProcessGroup using kill(-pid, …) — the prior syscall.Kill with a stale PID was already flagged by cursor, and the current code does check processGroupExists before the SIGKILL but still uses a stored PID that could theoretically be reused after waitpid; worth a human eye, though Firecracker's lifecycle makes this narrow in practice. iptables/nft cleanup now treats "not exist" errors as success, which is a behavioral change in the cleanup direction (more lenient) and not in the create direction.

Level of scrutiny

High. This is the orchestrator's hot shutdown path on a host that runs untrusted sandboxes — bugs here translate to leaked kernel resources, stuck drains, or zombie entries that mask real state. The new Map.lifecycles is a parallel index to live/network, and any divergence between the indexes shows up only under shutdown races. The PR author explicitly proposed a 24-hour traffic-simulator soak before merge, which is the right bar.

Other factors

The bug hunter surfaced a concrete non-atomic Get+Insert race in markLifecycleState that can resurrect a lifecycle entry after MarkStopped removed it — permanent zombie, slow leak compounded per occurrence, with a clean fix via the existing smap.Upsert callback that mirrors what MarkStopping already does on the live map. Separately, there are still open threads from djeebus (whether these helpers belong on lifecycle.Manager, dependency-injection-as-strings concern, the "close request ingress first" point doubling down on codex's P1) that don't appear resolved in the timeline. Test coverage is added but is unit-level — the integration soak the author described has not happened yet per the timeline.

Comment thread packages/orchestrator/pkg/sandbox/map.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 525136ce9f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/sandbox/network/firewall.go
Comment thread packages/orchestrator/pkg/template/server/create_template.go Outdated
Comment thread packages/orchestrator/pkg/server/main.go
Comment thread packages/orchestrator/pkg/template/server/create_template.go Outdated
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 525136c to f1b97d3 Compare June 3, 2026 19:17
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from f1b97d3 to 1054a90 Compare June 4, 2026 23:22
Comment thread packages/orchestrator/pkg/server/main.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1054a90419

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/orchestrator/pkg/server/main.go Outdated
Comment thread packages/orchestrator/pkg/server/main.go Outdated
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 1054a90 to 038dbc6 Compare June 5, 2026 04:08
Comment thread packages/orchestrator/pkg/server/main.go
@wj-e2b
wj-e2b force-pushed the infra-orchestrator branch from 642587e to 3c8efb1 Compare June 5, 2026 04:40
Comment thread packages/orchestrator/pkg/factories/run.go

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3c8efb1. Configure here.

Comment thread packages/orchestrator/pkg/factories/run.go
err := p.cmd.Process.Kill()
if err == nil {
logger.L().Info(ctx, "sent SIGKILL to fc process because it was not responding to SIGTERM for 10 seconds",
killErr := signalProcessGroup(pid, syscall.SIGKILL)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Agentic Security Review
Severity: MEDIUM
The shutdown path still sends SIGKILL to -pid after a delay based only on the stored numeric PID. If the original Firecracker process group exits and that PID is reused before the delayed kill path runs, this can terminate an unrelated process group.

Impact: tenant-triggerable sandbox churn can cause cross-sandbox availability impact by killing the wrong process group.

Fix in Cursor Fix in Web

Reviewed by Cursor Security Reviewer for commit 3c8efb1. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c8efb1a71

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

s.StartDraining(ctx)
// The sandbox factory is shared by API sandboxes and template-build sandboxes.
// Drain it before waiting so the lifecycle snapshot cannot miss a new build sandbox.
s.sandboxFactory.StartDraining(ctx)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Let accepted checkpoints finish before factory drain

When a graceful shutdown starts while a Checkpoint RPC is already past enterSandboxStart but still snapshotting the old sandbox, this closes the shared sandbox factory before waitSandboxStarts lets that in-flight RPC reach ResumeSandbox. The checkpoint then gets ErrFactoryDraining at the resume call, and because it has already MarkStopping'd the old sandbox and deferred stopSandboxAsync, the accepted checkpoint fails by tearing down the only live sandbox instead of letting the drain wait for it. Drain the factory only after the server start gate has quiesced for graceful shutdown, or otherwise allow already-entered starts to complete.

Useful? React with 👍 / 👎.

@wj-e2b wj-e2b closed this Jun 5, 2026
@wj-e2b
wj-e2b deleted the infra-orchestrator branch June 5, 2026 22:51
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.

2 participants