Skip to content

feat(gateway): implement wake-on-traffic for paused sandboxes#586

Open
furykerry wants to merge 8 commits into
openkruise:masterfrom
furykerry:feat/wake-on-traffic
Open

feat(gateway): implement wake-on-traffic for paused sandboxes#586
furykerry wants to merge 8 commits into
openkruise:masterfrom
furykerry:feat/wake-on-traffic

Conversation

@furykerry

@furykerry furykerry commented Jun 28, 2026

Copy link
Copy Markdown
Member

Ⅰ. Describe what this PR does

This PR implements wake-on-traffic for paused sandboxes in the sandbox-gateway. When a paused sandbox receives incoming traffic, the gateway automatically resumes it and forwards the request once the sandbox is running again.

Key changes:

  1. Wake-on-traffic in filter (pkg/sandbox-gateway/filter/filter.go): When a paused sandbox with WakeOnTraffic=true receives traffic, the filter launches wake in a goroutine with a detached context and returns api.Running to suspend Envoy request processing. The wakeAndContinue goroutine calls Continue(api.Continue) on success or SendLocalReply(503) on failure. Thread-safe completion state machine (mutex-protected) prevents double-reply when async goroutine and Destroy race.
  2. New wake package (pkg/sandbox-gateway/wake/wake.go): Implements Waker with retry logic for transient states (e.g., SandboxIsPausing). Calls sandbox.Resume() to patch the spec and waits for the sandbox to reach Running state via the cache wait reconciler. Uses errors.Is() with sentinel errors for error classification (not string matching). Singleflight deduplication: concurrent Wake calls for the same sandbox are coalesced via singleflight.Group so only one Resume is issued to the API server. Uses DoChan + select pattern so callers can bail out independently while shared work continues under a detached context.
  3. E2B pause with spec patch (pkg/servers/e2b/pause_resume.go): Adds Pause() that sets Spec.DesiredState=Paused (instead of deleting the sandbox), enabling the wake-on-traffic flow. updateWakeAnnotations patches wake annotations as a separate API call after Resume.
  4. Registry route lifecycle (pkg/sandbox-gateway/server/server.go): handleRefresh retains routes for all states except dead and empty string. This ensures the filter has full visibility (including paused, creating, available) for routing and wake decisions.
  5. Sentinel error classification (pkg/utils/utils.go, pkg/sandbox-manager/errors/error.go): Exports sentinel errors (ErrSandboxIsPausing, ErrSandboxIsTerminating, ErrShutdownTimeReached, ErrSandboxPhaseNotAllowed) from IsSandboxResumable. Adds Unwrap() + NewErrorWrap() to managererrors.Error to enable errors.Is() through wrapped errors.
  6. Gateway cache optimization (pkg/cache/cache.go): Restricts the gateway's informer cache to only Sandbox resources via ByObject filtering, reducing memory usage and API server load.
  7. Configuration: Adds ENABLE_WAKE_ON_TRAFFIC env var and wakeTimeoutSeconds config to the gateway filter.

Ⅱ. Does this pull request fix one issue?

NONE

Ⅲ. Describe how to verify it

  1. Run unit tests:
    go test ./pkg/sandbox-gateway/... ./pkg/cache/... ./pkg/servers/e2b/... ./pkg/utils/...
  2. Run E2E test:
    pytest test/e2b/test_wake_on_traffic.py
  3. Manual verification:
    • Create a sandbox, pause it via POST /sandboxes/{id}/pause
    • Send traffic to the paused sandbox
    • Verify the sandbox is automatically resumed and the request is forwarded

Ⅳ. Special notes for reviews

  • Async wake: DecodeHeaders returns api.Running when wake is triggered, launching wakeAndContinue in a goroutine. The goroutine uses a detached context (not tied to the Envoy filter lifecycle). On completion, it calls Continue(api.Continue) or SendLocalReply. The Destroy() method cancels the in-flight wake context when Envoy destroys the filter (stream reset). A mutex-protected completion state machine prevents double-reply races.
  • Singleflight: Waker.Wake() uses singleflight.Group with DoChan() to coalesce concurrent calls for the same sandbox. The shared Resume runs under a detached context, and callers can bail out via select on their own context.
  • handleRefresh: Only dead and empty-state routes are deleted from the registry. All other states (running, paused, creating, available) are retained for full filter visibility.
  • The Waker.Wake() retry loop only retries on ErrSandboxIsPausing (transient state). Other errors like ErrSandboxIsTerminating are non-retryable and return immediately.
  • The cache optimization (SandboxOnly mode) adds nil guards on GetSandboxController() and GetSandboxSetController() since controller handlers are not set up in that mode.

Ⅴ. Test coverage

Package Coverage
pkg/sandbox-gateway/filter 81.8%
pkg/sandbox-gateway/wake 95.6%
pkg/servers/e2b 87.7%
pkg/proxy 90.2%

Added tests:

  • TestIsEnvoyStreamGonePanic: table-driven test for panic detection (7 cases)
  • TestDestroyCancelsContext: verify Destroy cancels in-flight wake context
  • TestWakeAndContinueSuccess: verify async wake success path with Continue callback and upstream metadata
  • TestWakeSingleflight*: tests for singleflight deduplication
  • E2E test annotation lifecycle verification (test_wake_on_traffic.py)

@kruise-bot kruise-bot requested review from AiRanthem and zmberg June 28, 2026 06:06
@kruise-bot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from furykerry by writing /assign @furykerry in a comment. For more information see:The Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.30802% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.95%. Comparing base (6c7643a) to head (c6ab485).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
pkg/sandbox-gateway/filter/filter.go 63.35% 36 Missing and 12 partials ⚠️
...g/sandbox-gateway/controller/gateway_controller.go 0.00% 4 Missing ⚠️
pkg/cache/cache.go 40.00% 2 Missing and 1 partial ⚠️
pkg/cache/index.go 25.00% 2 Missing and 1 partial ⚠️
pkg/sandbox-gateway/server/server.go 0.00% 3 Missing ⚠️
pkg/sandbox-gateway/wake/wake.go 94.44% 2 Missing and 1 partial ⚠️
pkg/cache/cachetest/cachetest.go 0.00% 1 Missing ⚠️
...controller/sandboxclaim/sandboxclaim_controller.go 0.00% 1 Missing ⚠️
pkg/sandbox-manager/core.go 0.00% 1 Missing ⚠️
pkg/servers/e2b/routes.go 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #586      +/-   ##
==========================================
- Coverage   80.04%   79.95%   -0.09%     
==========================================
  Files         229      230       +1     
  Lines       17826    18031     +205     
==========================================
+ Hits        14268    14417     +149     
- Misses       2975     3019      +44     
- Partials      583      595      +12     
Flag Coverage Δ
unittests 79.95% <71.30%> (-0.09%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@furykerry furykerry force-pushed the feat/wake-on-traffic branch 9 times, most recently from 46fd168 to 02d8fdc Compare June 28, 2026 14:26
@furykerry furykerry force-pushed the feat/wake-on-traffic branch from eacd10c to 6d63285 Compare July 3, 2026 01:34
@furykerry furykerry force-pushed the feat/wake-on-traffic branch from 6d63285 to 9e742f7 Compare July 8, 2026 09:00
@furykerry furykerry force-pushed the feat/wake-on-traffic branch from d6db638 to b09a3d6 Compare July 9, 2026 05:50
Implement wake-on-traffic support in the sandbox-gateway Envoy Go filter,
allowing incoming HTTP requests to automatically resume paused sandboxes.

Key changes:
- Add wake-on-traffic annotation constants and config in sandbox-gateway
- Implement Wake() flow in filter: detect paused sandbox, call sandbox.Resume(),
  update registry, then forward request via ORIGINAL_DST
- Add informer cache fallback (HasWakeAnnotation) for annotation sync timing
- Set wake annotations at sandbox creation via autoResume in create.go
- Support AutoResume object format matching E2B SDK lifecycle parameter
- Add comprehensive unit tests for wake filter, waker, and config
- Add E2E test (test_wake_on_traffic.py) with access token auth
- Exclude wake-on-traffic test from non-gateway CI runs

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
… test

- Add singleflight.Group to coalesce concurrent Wake calls for the
  same sandbox, reducing duplicate API server Resume requests
- Use DoChan + select pattern so callers can bail out independently
  while shared work continues under a detached context
- Add 3 new concurrency tests: same-sandbox coalescing, different-
  sandbox isolation, and caller cancellation
- Expand E2E test to verify wake annotation lifecycle:
  - Check wake-on-traffic and wake-timeout-seconds annotations
    are set on the Sandbox CR after creation
  - Check annotations persist after wake (not stripped by Resume)
- Add _get_sandbox_annotations() and _request_gateway_until_forwarded()
  helpers for robust E2E verification

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
- DecodeHeaders now launches wake in a goroutine with detached context
  and returns api.Running to suspend Envoy request processing
- wakeAndContinue runs asynchronously: on success sets upstream metadata
  and calls Continue; on failure sends LocalReply (503)
- Add thread-safe completion state machine (mutex-protected) to prevent
  double-reply when async goroutine and Destroy race
- Add Destroy() to cancel in-flight wake context on stream reset
- Add isEnvoyStreamGonePanic helper to detect Envoy stream-gone panics
- Update mock callbacks to track Continue calls and signal completion
  via channel for async test synchronization
- Update TestDecodeHeadersWakeOnTrafficCacheFallback to expect
  api.Running and wait for async completion

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
- TestIsEnvoyStreamGonePanic: table-driven test for panic detection
- TestDestroyCancelsContext: verify Destroy cancels in-flight wake context
- TestWakeAndContinueSuccess: verify async wake success path with
  Continue callback and upstream metadata set

Coverage improved from 63.5% to 81.8% for filter package.

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
Add a sandboxOnly bool parameter to NewCache/NewCacheWithHealth,
SetupCacheControllersWithManager, and AddIndexesToCache. When true,
only Sandbox-related controllers (SandboxWaitReconciler and
SandboxCustomReconciler) and Sandbox field indexes are registered,
skipping Checkpoint, PVC, and SandboxSet controllers/indexes.

This allows sandbox-gateway to use cache.NewCache(mgr, true) without
requiring clientgoscheme registration, since corev1 types (PVC, PV)
are no longer needed.

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
@furykerry furykerry force-pushed the feat/wake-on-traffic branch from b09a3d6 to b71f030 Compare July 11, 2026 01:24
…affic test

GET /kruise/api/sandboxes returned 405 because the sandbox-manager only
registers POST /sandboxes (create) and GET /v2/sandboxes (list). The
manager has a dedicated GET /health endpoint that returns 200 OK — use
that instead for the gateway port-forward liveness check.

The test_sandbox_with_pod_ip failure is a separate flake (sandbox pod
died during pause/resume cycle), not related to this fix.

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
…t endpoints

Remove the AutoResumeConfig struct, autoResume fields from NewSandboxRequest,
NewSandboxRequestExtension, and SetTimeoutRequest. Remove updateWakeAnnotations
method and its calls from ResumeSandbox and ConnectSandbox handlers. Remove
related validation and annotation-setting code from create flow.

Wake-on-traffic annotations are now set directly on the Sandbox CR (e.g. via
kubectl) rather than through the autoResume API parameter. The E2E test is
updated to set annotations via kubectl after sandbox creation.

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
…check

The health endpoint was only registered as GET /health, not as
GET /kruise/api/health. When the E2E test sends a health check through
the sandbox-gateway (which forwards /kruise/api/* to the manager), the
manager returned 404 because the path was not registered.

Register the health handler under both /health and /kruise/api/health
so it works both directly and through the gateway.

Signed-off-by: 守辰 <shouchen.zz@alibaba-inc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants