-
Notifications
You must be signed in to change notification settings - Fork 3.1k
fix(e2e): admit sandbox runtime transition #11554
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| { | ||
| "contractVersion": 1, | ||
| "contractVersion": 2, | ||
| "jobId": "managed-image-protected-runtime", | ||
| "platform": "linux/amd64", | ||
| "providers": ["ollama", "nim", "vllm"], | ||
| "agents": ["openclaw", "hermes", "langchain-deepagents-code"] | ||
| "agents": ["openclaw", "hermes", "langchain-deepagents-code"], | ||
| "runtimeUser": "sandbox" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ let registryCurlExit = ""; | |
| let registryLog = ""; | ||
| let registryStatus = ""; | ||
| let teeFailureMode = ""; | ||
| let imageUser = ""; | ||
|
|
||
| function writeExecutable(name: string, source: string): void { | ||
| const target = path.join(stubBin, name); | ||
|
|
@@ -79,7 +80,16 @@ case "$*" in | |
| esac | ||
| ;; | ||
| "pull "*) ;; | ||
| "image inspect "*) printf '[]\n' ;; | ||
| "image inspect "*) | ||
| case "$*" in | ||
| *"/openclaw@"*) agent=openclaw ;; | ||
| *"/hermes@"*) agent=hermes ;; | ||
| *"/langchain-deepagents-code@"*) agent=langchain-deepagents-code ;; | ||
| *) exit 93 ;; | ||
| esac | ||
| printf '[{"Id":"sha256:${DIGEST}","Config":{"User":"%s","Labels":{"io.nvidia.nemoclaw.agent":"%s","io.nvidia.nemoclaw.managed-image.contract":"1","io.nvidia.nemoclaw.managed-image.platform":"%s","io.nvidia.nemoclaw.managed-image.startup-profile":"1","io.nvidia.nemoclaw.managed-image.capabilities":"1","io.nvidia.nemoclaw.managed-image.cohort":"protected-1-1","org.opencontainers.image.revision":"${REVISION}"}}}]\n' \ | ||
| "$NEMOCLAW_TEST_IMAGE_USER" "$agent" "$NEMOCLAW_TEST_IMAGE_PLATFORM" | ||
| ;; | ||
| *) exit 91 ;; | ||
| esac | ||
| `, | ||
|
|
@@ -90,7 +100,7 @@ esac | |
| case "$*" in | ||
| *containerimage.digest*) printf 'sha256:${DIGEST}\\n' ;; | ||
| *"if length == 1 then .[0].Id"*) printf 'sha256:${DIGEST}\\n' ;; | ||
| *"--arg agent "*) printf '{}\\n' ;; | ||
| *"--arg agent "*) PATH="$NEMOCLAW_TEST_REAL_PATH" command jq "$@" ;; | ||
| *"-se "*) printf '[]\\n' ;; | ||
| *) ;; | ||
| esac | ||
|
|
@@ -235,6 +245,8 @@ function recordedBuildInvocation(agent: string): string { | |
|
|
||
| function runBuild(sourceRoot: string, extraArgs: readonly string[] = [], platform = "linux/amd64") { | ||
| const output = path.join(testRoot, "contracts.json"); | ||
| const platformOverride = extraArgs.findIndex((argument) => argument === "--platform"); | ||
| const effectivePlatform = platformOverride >= 0 ? extraArgs[platformOverride + 1] : platform; | ||
| return spawnSync( | ||
| "bash", | ||
| [ | ||
|
|
@@ -265,6 +277,8 @@ function runBuild(sourceRoot: string, extraArgs: readonly string[] = [], platfor | |
| NEMOCLAW_TEST_DOCKER_BUILD_COUNT: dockerBuildCount, | ||
| NEMOCLAW_TEST_DOCKER_BUILD_FAILURE_MODE: dockerBuildFailureMode, | ||
| NEMOCLAW_TEST_DOCKER_LOG: dockerLog, | ||
| NEMOCLAW_TEST_IMAGE_PLATFORM: effectivePlatform, | ||
| NEMOCLAW_TEST_IMAGE_USER: imageUser, | ||
| NEMOCLAW_TEST_REGISTRY_CURL_EXIT: registryCurlExit, | ||
| NEMOCLAW_TEST_REGISTRY_LOG: registryLog, | ||
| NEMOCLAW_TEST_REGISTRY_STATUS: registryStatus, | ||
|
|
@@ -291,6 +305,7 @@ beforeEach(() => { | |
| registryLog = path.join(testRoot, "registry.log"); | ||
| registryStatus = "404"; | ||
| teeFailureMode = ""; | ||
| imageUser = "root"; | ||
| mkdirSync(stubBin); | ||
| writeExecutable( | ||
| "docker", | ||
|
|
@@ -338,6 +353,55 @@ describe("protected managed-image source-root boundary", () => { | |
| }); | ||
|
|
||
| describe("protected managed-image build-cache boundary", () => { | ||
| it("keeps the legacy root runtime contract unless a reviewed transition selects sandbox", () => { | ||
| stubBuildInvocation(); | ||
|
|
||
| const legacy = runBuild(REPO_ROOT); | ||
|
|
||
| expect(legacy.status, legacy.stderr).toBe(0); | ||
| expect(recordedBuildInvocations()).toHaveLength(3); | ||
| expect( | ||
| recordedBuildInvocations().every((invocation) => | ||
| invocation.includes("--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=root"), | ||
| ), | ||
| ).toBe(true); | ||
|
|
||
| writeFileSync(dockerLog, "", "utf8"); | ||
| imageUser = "sandbox"; | ||
| const transitioned = runBuild(REPO_ROOT, ["--runtime-user", "sandbox"]); | ||
|
|
||
| expect(transitioned.status, transitioned.stderr).toBe(0); | ||
| expect(recordedBuildInvocations()).toHaveLength(3); | ||
| expect( | ||
| recordedBuildInvocations().every((invocation) => | ||
| invocation.includes("--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=sandbox"), | ||
| ), | ||
| ).toBe(true); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Exercise rejection of a mismatched final image user.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 632e6ea. The Docker stub now returns a complete inspected-image record with the exact Config.User and contract labels, and the final jq branch delegates to the real jq binary so it executes the production predicate against that record. The root and sandbox success paths provide their exact expected users, and a new sandbox-selected/root-inspected case fails on the first image as required. Verification: build-script 35/35 locally; adjacent protected workflow + build-script 79/79 on Brev; all pre-commit and pre-push gates passed. |
||
| }); | ||
|
|
||
| it("rejects an image whose final Config.User does not match the selected runtime user", () => { | ||
| stubBuildInvocation(); | ||
| imageUser = "root"; | ||
|
|
||
| const result = runBuild(REPO_ROOT, ["--runtime-user", "sandbox"]); | ||
|
|
||
| expect(result.status, result.stderr).toBe(1); | ||
| expect(recordedBuildInvocations()).toHaveLength(1); | ||
| expect(recordedBuildInvocation("openclaw")).toContain( | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=sandbox", | ||
| ); | ||
| }); | ||
|
|
||
| it.each(["0", "Root", "sandbox\nroot"])( | ||
| "rejects unreviewed protected runtime user %j before invoking Docker", | ||
| (runtimeUser) => { | ||
| const result = runBuild(REPO_ROOT, ["--runtime-user", runtimeUser]); | ||
|
|
||
| expect(result.status, result.stderr).toBe(2); | ||
| expect(existsSync(dockerLog)).toBe(false); | ||
| }, | ||
| ); | ||
|
|
||
| it("passes the selected Buildx architecture explicitly to every Dockerfile", () => { | ||
| stubBuildInvocation(); | ||
|
|
||
|
|
@@ -347,12 +411,12 @@ describe("protected managed-image build-cache boundary", () => { | |
| expect(recordedBuildInvocation("openclaw")).toContain("--platform linux/arm64"); | ||
| expect(recordedBuildInvocation("openclaw")).toContain("--build-arg TARGETARCH=arm64"); | ||
| expect(recordedBuildInvocation("openclaw")).toContain( | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=sandbox", | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=root", | ||
| ); | ||
| expect(recordedBuildInvocation("hermes")).toContain("--platform linux/arm64"); | ||
| expect(recordedBuildInvocation("hermes")).toContain("--build-arg TARGETARCH=arm64"); | ||
| expect(recordedBuildInvocation("hermes")).toContain( | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=sandbox", | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=root", | ||
| ); | ||
| expect(recordedBuildInvocation("langchain-deepagents-code")).toContain( | ||
| "--platform linux/arm64", | ||
|
|
@@ -361,7 +425,7 @@ describe("protected managed-image build-cache boundary", () => { | |
| "--build-arg TARGETARCH=arm64", | ||
| ); | ||
| expect(recordedBuildInvocation("langchain-deepagents-code")).toContain( | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=sandbox", | ||
| "--build-arg NEMOCLAW_MANAGED_IMAGE_RUNTIME_USER=root", | ||
| ); | ||
| }); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.