cubeapi: accept e2b flattened autoPause/autoResume on sandbox create - #1254
Conversation
| } | ||
|
|
||
| #[test] | ||
| fn flat_lifecycle_fields_enable_pause_and_resume() { |
There was a problem hiding this comment.
These tests cover deserialization and the pure resolve_lifecycle_flags function separately, but nothing drives create_sandbox with flat autoPause/autoResume and asserts the flags actually reach the CubeMaster request. That's precisely the failure mode this PR fixes — before the change, the fields were silently dropped somewhere between inbound JSON and CreateSandboxRequest, and a future regression (e.g. someone removing the auto_pause: flat_auto_pause binding from the destructuring at line ~158, which still compiles because of ..) would not be caught by any of these unit tests.
The existing mock-CubeMaster integration tests (e.g. create_sandbox_forwards_create_time_env_vars_to_cubemaster, which captures the outbound request body) are the right template: a parallel test sending autoPause: true / autoResume: {"enabled": true} and asserting create_body["auto_pause"] == true / create_body["auto_resume"] == true would lock the full path down. The destructuring binding → resolve call → request field assignment is currently only covered by code review, not a test.
| /// explicitly it wins over the flattened compatibility fields. The e2b SDK | ||
| /// never sends both, so this only disambiguates direct API callers. | ||
| #[test] | ||
| fn nested_lifecycle_wins_over_flat_lifecycle_fields() { |
There was a problem hiding this comment.
Since resolve_lifecycle_flags is now extracted as a pub(crate) function, the pre-existing lifecycle_object_translates_to_cubemaster_bools test (just above these additions) still carries its own inline translate helper that re-implements the nested-lifecycle branch of the new function. It could call the real code directly, e.g. resolve_lifecycle_flags(body.lifecycle.as_ref(), None, None). As-is, the test and the production logic are two copies that can silently drift — the exact kind of stale-duplication hazard the pure function was extracted to remove. Consider updating that test while this is fresh.
Review: cubeapi — accept e2b flattened autoPause/autoResume on sandbox createAI-generated review. This is a machine assessment; please treat it as input, not a human approval. Verdict: Looks correct — approve with minor nitsThe fix is sound. Things I verified against the base tree:
Should address
Design points raised inline
Minor
|
|
Thanks for your contribution. |
|
please squash all commits into a single commit. @Arpit-Ahuja-293 |
| - **`auto_resume=True`**: when any request next arrives for a `paused` sandbox (HTTP, `run_code`, file I/O, …), the platform wakes it up before the request lands. Callers never see the pause; typical resume latency is sub-second to a few seconds. | ||
| - If `auto_resume=False` (or unset), the sandbox stays paused until you explicitly `Sandbox.connect(sandbox_id=...)`. Useful for "wait for the user" workflows. | ||
|
|
||
| ### Wire format |
There was a problem hiding this comment.
I think there is no need to update the docs. This is implementation detail, not user-facing APIs.
|
@Arpit-Ahuja-293 please remove the lifecycle doc change commit |
The e2b SDK flattens its user-facing lifecycle object into top-level autoPause / autoResume before the request goes out. NewSandbox bound neither field, so serde dropped them and CubeAPI forwarded auto_pause=false — sandboxes were killed at timeout instead of paused. Bind both fields and resolve them alongside the nested lifecycle object, which keeps precedence when a caller sends it. autoResume accepts both the object and bare-bool wire shapes. Closes TencentCloud#965 Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Arpit Ahuja <iamarpitahuja@gmail.com>
Signed-off-by: jinlong <jinlong@tencent.com>
478b4d2 to
a616958
Compare
|
|
||
| /// e2b SDK compatibility: the SDK flattens its user-facing `lifecycle` | ||
| /// object into top-level `autoPause` / `autoResume` before it hits the | ||
| /// wire, so `lifecycle` never arrives from an SDK caller. Ignored when |
There was a problem hiding this comment.
Nit on the doc comment: "so lifecycle never arrives from an SDK caller" is only true of the e2b SDK. CubeSandbox's own Python SDK (sdk/python/cubesandbox/sandbox.py:_serialize_lifecycle) sends the nested lifecycle object (onTimeout/autoResume) on every create, and the existing lifecycle_object_translates_to_cubemaster_bools test covers exactly that path — which is precisely why the "nested wins" precedence matters. Suggest narrowing to something like "the e2b SDK flattens this client-side, so the nested lifecycle is what CubeSandbox's own SDK and direct API callers send."
| auto_pause: Option<bool>, | ||
| auto_resume: Option<&SandboxAutoResume>, | ||
| ) -> (bool, bool) { | ||
| if let Some(lc) = lifecycle { |
There was a problem hiding this comment.
Precedence edge worth a decision since the rule is introduced here: lifecycle deserializes to Some(SandboxLifecycleConfig::default()) for an empty object {} (both fields have #[serde(default)]). So a request carrying lifecycle: {} plus an explicit autoPause: true resolves to (false, false) and the flat flag is silently dropped — even though {} reads more like "caller didn't actually specify a policy" than an intentional Kill choice. Not a bug per the documented rule (the SDK never sends both), but it's the one case where "nested wins" drops a non-default flat value; worth confirming that's intended.
Problem
The e2b Python SDK does not put
lifecycleon the wire.Sandbox.create(lifecycle={"on_timeout": "pause", "auto_resume": True})is flattened client-side into top-level fields beforePOST /sandboxes:{ "templateID": "<id>", "timeout": 30, "autoPause": true, "autoResume": { "enabled": true } }NewSandboxbound neither field, so serde dropped them silently. CubeAPI then resolvedlifecycle = Noneand forwardedauto_pause=falseto CubeMaster — the sandbox was killed at timeout instead of paused. The setting appeared to succeed at the SDK layer and did nothing.Fix
NewSandboxbindsautoPauseandautoResume.SandboxAutoResumeuntagged enum accepts both{"enabled": true}(current SDK) and baretrue(older releases, hand-rolled clients) — same meaning, so no reason to 400 on shape.create_sandboxintoresolve_lifecycle_flags(), a pure function that unit tests can reach.Design decision worth reviewing
When a request carries both
lifecycleand the flattened fields, the nestedlifecycleobject wins.Reasoning: the e2b SDK never sends both, so this only disambiguates direct API callers — and an explicit
lifecyclecan only come from someone who meant it. Happy to invert this if maintainers prefer the flat fields taking precedence.Tests
7 lifecycle tests, 5 of them new, written test-first:
new_sandbox_deserializes_e2b_flat_lifecycle_fields— the exact e2b wire bodynew_sandbox_accepts_bare_bool_auto_resumeflat_lifecycle_fields_enable_pause_and_resumenested_lifecycle_wins_over_flat_lifecycle_fieldsabsent_lifecycle_fields_keep_kill_behaviour— no behaviour change for callers that don't opt inVerification
Run in the project builder image (
rustc 1.89,aarch64-unknown-linux-musl):make cube-api-test→ 108 passed, 0 failedmake cubeapi→ clean release buildcargo fmt --check→ cleancargo clippy --all-targets→ warning set identical to the pre-change baseline; none in new codecargo build --lockedpasses — no dependency changes,Cargo.lockuntouchedAlso updated
openapi.yml— regenerated viacube-api --export-openapi, applying only the additive schema hunks so the hand-maintained license header andservers:block survive.docs/guide/lifecycle.mdanddocs/zh/guide/lifecycle.md— new "Wire format" section covering both accepted shapes and the precedence rule, EN and ZH in sync.Closes #965