diff --git a/crates/cloud-sdk/src/sandbox_images.rs b/crates/cloud-sdk/src/sandbox_images.rs index f057bd46..cfc38b46 100644 --- a/crates/cloud-sdk/src/sandbox_images.rs +++ b/crates/cloud-sdk/src/sandbox_images.rs @@ -55,12 +55,6 @@ const ROOTFS_BUILDER_PATH: &str = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" const ROOTFS_BUILDER_COMMAND: &str = "tl-rootfs-build"; const ROOTFS_BUILDER_PROCESS_USER: &str = "root"; -/// Dockerfile instructions rejected during sandbox-image build. The rootfs -/// builder hands the Dockerfile to `docker build` verbatim, so without this -/// allowlist these instructions would silently take effect inside the -/// resulting rootfs. Mirrors the historical Python/TS SDK behavior. -const UNSUPPORTED_DOCKERFILE_INSTRUCTIONS: &[&str] = &["ARG", "ONBUILD", "SHELL", "USER"]; - /// Dockerfile instructions that affect image config (CMD, ENTRYPOINT, etc.) /// rather than the filesystem. `docker build --output type=tar` discards the /// image config, so these are effectively no-ops on the rootfs path — we warn @@ -1181,12 +1175,6 @@ fn load_dockerfile_text_plan( base_image = Some(parse_from_value(&value, line_number)?); continue; } - if UNSUPPORTED_DOCKERFILE_INSTRUCTIONS.contains(&keyword.as_str()) { - return Err(SandboxImageBuildError::other(format!( - "line {}: Dockerfile instruction '{}' is not supported for sandbox image creation", - line_number, keyword - ))); - } if IGNORED_DOCKERFILE_INSTRUCTIONS.contains(&keyword.as_str()) { ignored_instructions.push((line_number, keyword)); } @@ -1507,32 +1495,22 @@ mod tests { } #[test] - fn load_dockerfile_plan_rejects_unsupported_instructions() { - for instruction in [ - "ARG FOO=1", - "ONBUILD RUN echo", - "SHELL [\"/bin/bash\"]", - "USER app", - ] { - let temp_dir = tempfile::tempdir().unwrap(); - let dockerfile_path = temp_dir.path().join("Dockerfile"); - std::fs::write( - &dockerfile_path, - format!("FROM python:3.12-slim\n{}\n", instruction), - ) - .unwrap(); - - let error = load_dockerfile_plan(&dockerfile_path, None).unwrap_err(); - let keyword = instruction.split_whitespace().next().unwrap(); - let expected = format!( - "line 2: Dockerfile instruction '{}' is not supported for sandbox image creation", - keyword - ); - assert!( - error.to_string().contains(&expected), - "instruction {instruction}: expected {expected:?} in {error}", - ); - } + fn load_dockerfile_plan_preserves_builder_interpreted_instructions() { + let temp_dir = tempfile::tempdir().unwrap(); + let dockerfile_path = temp_dir.path().join("Dockerfile"); + let dockerfile_text = "FROM python:3.12-slim\n\ + ARG FOO=1\n\ + SHELL [\"/bin/bash\", \"-lc\"]\n\ + RUN echo \"$FOO\"\n\ + USER app\n\ + ONBUILD RUN echo child\n"; + std::fs::write(&dockerfile_path, dockerfile_text).unwrap(); + + let plan = load_dockerfile_plan(&dockerfile_path, None).unwrap(); + + assert_eq!(plan.base_image, "python:3.12-slim"); + assert_eq!(plan.dockerfile_text, dockerfile_text); + assert!(plan.ignored_instructions.is_empty()); } #[test] diff --git a/src/tensorlake/image/sandbox_builder.py b/src/tensorlake/image/sandbox_builder.py index 557f5bf1..d5e2246f 100644 --- a/src/tensorlake/image/sandbox_builder.py +++ b/src/tensorlake/image/sandbox_builder.py @@ -8,9 +8,10 @@ This is the programmatic backend for :meth:`Image.build` and the ``tl sbx image create`` CLI command. The Rust core owns parsing, the -Dockerfile-instruction allowlist (``ARG``/``ONBUILD``/``SHELL``/``USER`` are -rejected) and ignored-set warnings (``CMD``/``ENTRYPOINT``/``EXPOSE``/ -``HEALTHCHECK``/``LABEL``/``STOPSIGNAL``/``VOLUME``). +single-stage ``FROM`` validation, and ignored-set warnings +(``CMD``/``ENTRYPOINT``/``EXPOSE``/``HEALTHCHECK``/``LABEL``/``STOPSIGNAL``/ +``VOLUME``). All other Dockerfile instructions are preserved for the rootfs +builder to interpret. """ from __future__ import annotations diff --git a/typescript/src/sandbox-image.ts b/typescript/src/sandbox-image.ts index 4e80a7fa..d8b0c6ed 100644 --- a/typescript/src/sandbox-image.ts +++ b/typescript/src/sandbox-image.ts @@ -10,9 +10,10 @@ import { Image, dockerfileContent } from "./image.js"; * Renders the source to a Dockerfile (for `Image` inputs) and hands the * Dockerfile path/text + context to the Rust core via `@tensorlake/native`, * which parses, validates, materializes, and registers the image. The Rust - * core owns parsing, the Dockerfile-instruction allowlist - * (`ARG`/`ONBUILD`/`SHELL`/`USER` are rejected) and ignored-set warnings + * core owns single-stage `FROM` validation and ignored-set warnings * (`CMD`/`ENTRYPOINT`/`EXPOSE`/`HEALTHCHECK`/`LABEL`/`STOPSIGNAL`/`VOLUME`). + * All other Dockerfile instructions are preserved for the rootfs builder to + * interpret. */ export interface CreateSandboxImageOptions {