fix(sdk): make Sandbox.create(env_vars) visible to commands.run#484
fix(sdk): make Sandbox.create(env_vars) visible to commands.run#484xiaojunxiang2023 wants to merge 1 commit into
Conversation
|
Claude finished @xiaojunxiang2023's task in 58s —— View job Review: fix(sdk): make Sandbox.create(env_vars) visible to commands.runSummaryThis is a well-structured, multi-layer fix that ensures Layer-by-layer analysisCubeAPI (Rust —
|
| Test | Layer | Covers |
|---|---|---|
build_containers_from_env_vars_maps_sdk_payload |
CubeAPI | Payload mapping |
TestApplyTemplateToContainerCreateTimeEnvOverridesTemplateDefaults |
CubeMaster | Env merge order |
TestApplyTemplateToContainerPreservesExplicitCommandAndArgs |
CubeMaster | Command/args fallback |
TestGenCreateEnvProfileExportsCreateTimeVars |
Cubelet | Profile generation |
TestCreateNetfilesAddsCreateEnvProfile |
Cubelet | Netfile integration |
TestEnsureCreateEnvProfileRefreshesFromLatestEnvs |
Cubelet | Late-binding refresh |
test_create_env_vars_available_in_commands_run |
SDK E2E | Full integration |
Tests cover both the happy path and edge cases (empty envs, internal var filtering, explicit commands preserved). Good testing.
Final verdict
The PR is well-designed and correctly addresses the problem at every layer. The architecture is sound — passing env vars through the container spec, merging at CubeMaster, and injecting into profile.d for bash login shells.
The restore-path per-container env sharing bug (🔴 High) should be addressed before merging. The shell injection risk (🟡 Medium) is worth fixing for defense-in-depth, especially if user-provided values may contain shell metacharacters.
Needs changes: 1 bug fix recommended before merge.
272bde0 to
38acf70
Compare
|
Claude finished @xiaojunxiang2023's task in 1m 24s —— View job Re-review - PR updated (commit
|
| Before | After |
|---|---|
Single prepared boolean skips all subsequent containers |
Per-container hostdir: each container gets its own volume name via CreateEnvVolumeName(containerKey) → cube-internal-create-env-<containerKey> |
| One bind mount shared by all containers | Per-container bind mounts: each container writes to its own hostdir (/data/cubelet/create-env/<sandboxID>/<containerKey>) and gets its own bind target (shareDir/<volumeName>) |
appendCreateEnvVolumeMount uses hardcoded volume name |
Parameterized: appendCreateEnvVolumeMount(containerReq, volumeName) now takes the per-container volume name |
storageInfo.HostDirBackendInfos uses one key |
Per-container keys: volumeName + "/profile" — each container registers its own backend info entry |
Files: cube_container_create.go:540-594, netfile.go:176-181
🟡 Medium: Shell injection risk in genCreateEnvProfile — FIXED ✅
| Before | After |
|---|---|
strconv.Quote(kv.Value) — double-quoted Go string literal; $ and backtick pass through unescaped |
shellSingleQuoted(kv.Value) — POSIX single-quote wrapping with proper embedded-quote escaping (' → '\'') |
export FOO="hello $USER" → $USER expands when sourced |
export FOO='hello $USER cmd '\''quote'\''' → all metacharacters literal |
The shellSingleQuoted function handles the ' → '\'' escape correctly: it closes the single-quoted string, inserts an escaped single quote, then re-opens.
Files: netfile.go:353-356, 375
🟡 Medium: Data race in EnsureCreateEnvProfile — FIXED ✅
- Added
sync.Mutexfield toCubeboxNetfilestruct (netfile.go:66) EnsureCreateEnvProfileacquires the lock (netfile.go:190-191)
File: netfile.go:66, 190-191
⚪ Low: Hardcoded path in restore function — FIXED ✅
- Exported
HostDirBasePathfromstoragepackage (hostdir.go:23) injectCreateEnvProfileForRestorenow usesstorage.HostDirBasePathinstead of the literal/data/cubelet/hostdir(cube_container_create.go:534)
⚪ Low: Config path change in sandboxutil_test.go
Still present (../../../../test/conf.yaml → ../../../../conf.yaml). This looks like a CI working directory alignment — the test config is expected at a specific depth from the test binary's CWD.
New observations
- Cleanup:
cleanupHostDirVolumes(hostdir.go:144-147) now properly cleans up the per-sandboxcreate-envdirectory withos.RemoveAll. - Test coverage: The new
TestCreateEnvVolumeNametest (netfile_test.go:395-398) covers both empty and non-empty container key cases — good edge case coverage. - Single quote escaping test: The
SHELL_LITERALtest case (netfile_test.go:386-390) verifies that$, backticks, and embedded single quotes are all handled correctly.
Final verdict
The PR is ready to merge. All previous review findings have been addressed, and the fixes are well-implemented:
- Per-container env profiles in the restore path (proper volume isolation)
- Shell-safe single-quote escaping (defense-in-depth)
- Mutex-protected netfile updates (thread safety)
- Exported path variable (consistency)
No new issues were introduced.
|
You missed DCO line in your commit, you can use git commit --amend -s to add DCO line to your commit |
38acf70 to
f143476
Compare
|
Claude finished @xiaojunxiang2023's task in 1m 18s —— View job Re-review: fix(sdk): make Sandbox.create(env_vars) visible to commands.run✅ DCO CheckThe commit ( No action needed — DCO is satisfied. ✅ Previous Review Findings — All VerifiedThe four actionable issues from the previous review have all been addressed in the current codebase:
✅ Additional Verification
Final VerdictReady to merge. The DCO is present, all previous review findings have been resolved, and no new issues were introduced. |
There was a problem hiding this comment.
Sorry, I accidentally committed some irrelevant files from a local temporary build. They have been reverted.
fslongjin
left a comment
There was a problem hiding this comment.
Your PR accidently reverts some changes in master. It seems you may have made a mistake during the code merge. Please review the PR again and make sure it only contains the necessary changes.
f143476 to
3f2f932
Compare
|
Claude finished @xiaojunxiang2023's task in 4m 25s —— View job Review of reviewer's CHANGES_REQUESTEDI've investigated the two concerns raised by @fslongjin. Issue 1:
|
Inject create-time env vars via per-container restore bind mounts and POSIX single-quoted profile exports in Cubelet. Extend CubeAPI sandbox create mapping, add Cubelet netfile/cubebox helpers, and cover SDK plus Cubelet behavior with tests. Signed-off-by: xiaojunxiang <xiaojunxiang@kingsoft.com>
7e0d0c7 to
ef43307
Compare
|
Claude finished @xiaojunxiang2023's task in 57s —— View job Final Assessment — All Issues Resolved ✅TODO
Verification Results1. Accidental revert in
|
| Issue | Status | Evidence |
|---|---|---|
| 🔴 Restore path shares one env profile across all containers | Verified Fixed | CreateEnvVolumeName(containerKey) per-container volume isolation (cube_container_create.go:553) |
🟡 Shell injection risk in genCreateEnvProfile |
Verified Fixed | shellSingleQuoted() uses POSIX single-quote wrapping (netfile.go:353-354) |
🟡 Data race in EnsureCreateEnvProfile |
Verified Fixed | sync.Mutex added to CubeboxNetfile (netfile.go:66, 190-191) |
| ⚪ Hardcoded path in restore function | Verified Fixed | storage.HostDirBasePath exported and used (hostdir.go:23, cube_container_create.go:534) |
⚪ Config path in sandboxutil_test.go |
No longer in diff | Restored to ../../../../test/conf.yaml |
4. Current Diff Summary
The diff spans 9 files with +547/-18 lines, all correctly scoped to the env vars injection feature:
- CubeAPI (
sandboxes.rs):build_containers_from_env_varsmaps SDKenvVars→ placeholderContainerSpec - CubeMaster (
cubeboxutil.go): Merges create-time envs with template defaults (last-one-wins), preserves command/args for env-only placeholders - Cubelet netfile (
netfile.go):genCreateEnvProfilewithshellSingleQuotedexports,EnsureCreateEnvProfilelate-binding refresh,WriteCreateEnvProfileToBlockDevicefor rootfs injection - Cubelet cube_container_create (
cube_container_create.go): Per-container restore path with isolated bind mounts, cleanup - Cubelet hostdir (
hostdir.go): Sandbox-level cleanup of create-env directories - SDK (
test_env_injection_e2e.py): End-to-end test
Final Verdict
Ready to merge. All previously identified issues have been resolved. The changes are correctly scoped to the env vars injection feature across all four layers (CubeAPI → CubeMaster → Cubelet → SDK), and no new issues were introduced.
Sorry, I accidentally committed some irrelevant files from a local temporary build. They have been reverted. |
fslongjin
left a comment
There was a problem hiding this comment.
Thanks for working on this and for adding the test coverage. However, I do not think we should merge this PR in its current form.
The current implementation injects create-time environment variables by writing /etc/profile.d/99-cube-create-env.sh, so that later commands.run(...) calls using a login shell can read them. This only covers part of the problem: it mainly affects commands launched later through the SDK command API. It does not truly refresh the runtime environment for existing processes inside the sandbox, nor for child processes that those in-sandbox processes may start themselves. In other words, this does not solve environment synchronization at the sandbox/runtime layer after snapshot restore.
The technical approach also feels too hacky and introduces undesirable coupling. It ties environment variable semantics to the guest rootfs, shell profile behavior, login-shell startup, and special snapshot restore handling. That creates several risks:
env_varsmay contain tokens or secrets; writing them into profile/rootfs files expands their lifetime and creates plaintext-at-rest exposure.- The profile
exportstatements may override per-command environment variables passed viacommands.run(..., env=...), which would break the expected precedence semantics. - Environment variable keys are interpolated directly into a shell script, so they would require strict validation to avoid shell parsing or injection issues.
- The implementation adds coupling across normal start, restore, netfile, hostdir, and rootfs mutation paths, increasing maintenance cost and failure surface.
- The behavior depends on
/bin/bash -land/etc/profile.d, so it is not a general solution for non-login shells, non-bash entrypoints, or other process models inside the sandbox.
I think the better direction is to treat env_vars as sandbox runtime metadata. Cubelet should synchronize that metadata into the guest runtime through a guest agent/envd init or resume-sync mechanism after create/restore. Then the agent/exec layer should merge environment variables when creating new processes, with a clear precedence such as:
image env < template env < sandbox create env < per-command env
That approach matches a VM snapshot sandbox architecture much better and avoids persisting runtime configuration into rootfs/profile files.
So I am requesting changes here. The API/Master-side propagation of envVars may be a useful part of the solution, but I do not think the Cubelet profile/rootfs/bind-mount injection approach should be merged.

Summary
Fix
Sandbox.create(env_vars={...})so injected variables are visible to subsequentcommands.run(...)calls, matching the SDK field description: Environment variables injected into the sandbox.Problem
Sandbox.create(env_vars={...})
env_varswere not reliably available inside the sandbox for login-shell commands.Solution
envVarsinto the create sandbox payloadcommand/argsfor env-only placeholderscommands.run(bash -l -c) can read themTest plan
build_containers_from_env_vars_maps_sdk_payloadTestApplyTemplateToContainerCreateTimeEnvOverridesTemplateDefaultsTestApplyTemplateToContainerPreservesExplicitCommandAndArgsTestGenCreateEnvProfileExportsCreateTimeVarsTestCreateNetfilesAddsCreateEnvProfileTestEnsureCreateEnvProfileRefreshesFromLatestEnvsTestCreate::test_create_sends_env_varstests/test_env_injection_e2e.pyfix then deploy
Signed-off-by: xiaojunxiang xiaojunxiang@kingsoft.com