Skip to content

feat(cloud-tasks): add multi-arch OCI image target#431

Open
balajinvda wants to merge 5 commits into
mainfrom
feat/cloud-tasks-oci-image
Open

feat(cloud-tasks): add multi-arch OCI image target#431
balajinvda wants to merge 5 commits into
mainfrom
feat/cloud-tasks-oci-image

Conversation

@balajinvda

@balajinvda balajinvda commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Why

cloud-tasks had no container image target under Bazel. nvct-service/BUILD.bazel declared only the Spring Boot jar (spring_boot_app(name = "app")), with no oci_image/oci_image_index anywhere in the subtree, so the service could not be released by any mechanism: the release backend's oci_push had nothing to point at.

This adds the missing piece so cloud-tasks containers can be cut.

What changed

  • rules/oci: new java_oci_image macro (there was only go_oci_image). Packages a runnable jar and emits the same target set as the Go macro, including the _index multi-arch label that release tooling pushes.
  • MODULE.bazel: pull nvcr.io/nvidia/distroless/java (25-jdk-v4.0.8) pinned by digest, matching the distroless_go / distroless_cc convention and the base the pre-Bazel nvct-service/Dockerfile used.
  • nvct-service/BUILD.bazel: java_oci_image target producing nvct-service-oss.

Two base-image details are encoded in the macro so callers cannot get them wrong:

  1. The distroless bases set ENTRYPOINT ["/usr/bin/shelless_ulimit"], a shim that raises the soft file-descriptor limit, and oci_image REPLACES rather than appends the base entrypoint. Omitting the shim silently leaves the container on the default 1024 fd soft limit. This is the same issue grpc-proxy hit for its Go image.
  2. java is invoked via /usr/bin/java, not via JAVA_HOME. The base sets JAVA_HOME per-architecture (/usr/lib/jvm/temurin-25-jdk-amd64 vs -arm64), so a JAVA_HOME-derived path would break the arm64 half of the image index. /usr/bin/java is present on both arches (verified in the image layers).

Customer Release Notes

Not customer visible yet. This adds the build target; no image is published until the release wiring lands.

Testing

bazel build //src/control-plane-services/cloud-tasks/nvct-service:nvct-service-oss-image_index succeeds (1734 actions). Verified on the built artifact:

  • Index media type application/vnd.oci.image.index.v1+json with both linux/amd64 and linux/arm64/v8.
  • Entrypoint on both arches: ['/usr/bin/shelless_ulimit', '/usr/bin/java', '-jar', '/app.jar'].

Notes

This is the build target only. Cutting an actual cloud-tasks release still needs, separately: a version source and a release: block in tools/ci/subproject-validations.yaml, and a config/services/cloud-tasks.yaml in nvcf-internal. nvcf-internal MR 96 adds the source.module_root support that lets a root-module service such as cloud-tasks stage a release without first splitting the Java tree into standalone modules.

Push the _index label, never :nvct-service-oss-image, which is the single host-arch image.

References

None

Related Merge Requests/Pull Requests

nvcf-internal MR 96 (root-module support in the Bazel staging build).

Dependencies

None. The distroless java base is an existing NVIDIA public image, pinned by digest.

Summary by CodeRabbit

  • New Features

    • Added multi-architecture OCI image building for Java services using a distroless Java base.
    • Introduced a reusable Java OCI packaging option to place the app JAR in a fixed in-image location and generate the expected JVM-based entrypoint.
    • Added an OCI image build target for the NVCT service, including the runtime environment settings required by the prior container contract.
  • Tests

    • Extended and hardened the image contract test to validate the JAR location, ensure the entrypoint matches exactly, and confirm required runtime metadata (ULIMIT/JVM options and working directory).

cloud-tasks had no container image target under Bazel: nvct-service declared
only the Spring Boot jar, so it could not be released by any mechanism. Add
the missing piece.

- rules/oci: new java_oci_image macro, mirroring go_oci_image. Packages a
  runnable jar and emits the same target set, including the _index multi-arch
  label that release tooling pushes.
- MODULE.bazel: pull nvcr.io/nvidia/distroless/java (25-jdk-v4.0.8) pinned by
  digest, matching the distroless_go / distroless_cc convention and the base
  the pre-Bazel nvct-service Dockerfile used.
- nvct-service: java_oci_image target producing nvct-service-oss.

Two base-image details the macro encodes so callers cannot get them wrong:
the distroless entrypoint is the shelless_ulimit shim and oci_image REPLACES
rather than appends the base entrypoint, so the default entrypoint keeps the
shim (dropping it silently leaves the container on a 1024 fd soft limit, the
same issue grpc-proxy hit); and java is invoked via /usr/bin/java rather than
JAVA_HOME, which the base sets per-architecture and would break arm64.

Verified: bazel query resolves
//src/control-plane-services/cloud-tasks/nvct-service:nvct-service-oss-image_index

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda requested review from a team as code owners July 24, 2026 20:43
@balajinvda
balajinvda requested a review from dmikhaylovnv July 24, 2026 20:43
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds reusable Java OCI image support with a pinned multi-architecture Distroless Java base, default Java entrypoint construction, environment and working-directory forwarding, an nvct-service release image target built from its executable JAR, and contract validation for the resulting image.

Changes

Java OCI image support

Layer / File(s) Summary
Distroless Java base wiring
MODULE.bazel
Registers the pinned distroless_java image and its amd64 and arm64 repositories.
Java OCI image rule and image metadata propagation
rules/oci/private/java.bzl, rules/oci/private/common.bzl, rules/oci/defs.bzl
Adds the exported java_oci_image macro, packages the JAR at the configured path, constructs the default Java entrypoint, and forwards environment and working-directory settings.
nvct-service image and contract validation
src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel, src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
Defines nvct-service-oss-image with the JAR at /usr/share/app.jar, runtime settings, and contract checks for the archive, entrypoint, environment, JVM flags, and working directory.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: dmikhaylovnv

Sequence Diagram(s)

sequenceDiagram
  participant nvct_service
  participant java_oci_image
  participant pkg_tar
  participant create_oci_image
  participant distroless_java
  participant image_contract_test
  nvct_service->>java_oci_image: Provide executable JAR and runtime settings
  java_oci_image->>pkg_tar: Package JAR at /usr/share/app.jar
  java_oci_image->>create_oci_image: Provide layer, entrypoint, env, and workdir
  distroless_java->>create_oci_image: Provide multi-arch base image
  image_contract_test->>create_oci_image: Inspect generated image tar
  create_oci_image-->>image_contract_test: Return layers and runtime metadata
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately describes the main change: adding a multi-arch OCI image target for cloud-tasks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cloud-tasks-oci-image

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rules/oci/private/java.bzl`:
- Around line 95-99: Align the custom base-image contract with the emitted
entrypoint: update the documentation for the “base” attribute to require Java at
/usr/bin/java, or change the generated command to use a configurable Java path.
Keep the contract and the actual startup command consistent.

In `@src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel`:
- Around line 54-58: Add a repository-native test target alongside the
nvct-service-oss-image definition to verify the generated multi-architecture
image index, including expected platform manifests and the configured
entrypoint. Use existing image-index test utilities or conventions, and ensure
the target is included in the standard test runner; otherwise document in the PR
why automated coverage is not applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f71fa8c6-dfbd-493c-bf7f-00a3667feecb

📥 Commits

Reviewing files that changed from the base of the PR and between 698e3e9 and 8e3ca95.

📒 Files selected for processing (4)
  • MODULE.bazel
  • rules/oci/defs.bzl
  • rules/oci/private/java.bzl
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel

Comment thread rules/oci/private/java.bzl
Comment thread src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel Outdated
Generated by bazel; additive only (the distroless_java oci_pull entries plus
the usagesDigest). No existing pin changes.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>

@sanjay-saxena sanjay-saxena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@balajinvda -- I asked Codex to review the PR and here are its findings:

Findings

Blocking: Docker runtime parity is incomplete.

  • java.bzl preserves the shelless_ulimit executable but not ULIMIT_FLAG=1. Without that variable, the shim does not raise the file-descriptor limit. It also drops Cloud Tasks’ JDK_JAVA_OPTIONS and /home/app working directory from the existing Dockerfile.

  • The shared macro should support env and workdir; Apps should supply:

    • ULIMIT_FLAG=1
    • Existing JDK_JAVA_OPTIONS
    • workdir = "/home/app"

Important: jar_path does not control where the JAR is installed.
pkg_tar preserves the source JAR basename, while jar_path only changes the entrypoint. A future caller using a JAR not named app.jar, or setting /usr/share/app.jar, could produce an image whose entrypoint references a nonexistent file. The packaging rule should explicitly rename/place the JAR at jar_path, or remove that configurable attribute and enforce /app.jar.

Important: CI is not testing the image.
The image targets inherit the manual tag, so the green Bazel lanes do not prove the image can be built or started. The PR only records a local build. I agree with CodeRabbit that there should be a repository-native test or explicit CI step validating:

  • Both architectures exist.
  • Entrypoint, environment and working directory are correct.
  • /app.jar exists.
  • Ideally, the host-architecture image starts successfully.

Scope clarification: this PR does not publish images yet.
The PR description acknowledges this. It creates the _index artifact needed by release tooling, but:

  • Cloud Tasks is not selectable in image-push-manual.yml.
  • No public release descriptor/version source is added.
  • The internal release configuration is still separate.
    I would describe this PR as “Java OCI build support,” with publishing completed and proven by its follow-up.

Minor: custom base contract is misleading.
The base documentation says it needs a JRE on PATH, but the generated entrypoint specifically requires /usr/bin/java and /usr/bin/shelless_ulimit. Either document that exact contract or make those paths configurable.

Recommendation
I would approve the overall design after findings 1–3 are addressed. The root-level distroless Java pin is appropriate shared infrastructure, not a Cloud Tasks dependency leak. The reusable rule is also the right abstraction; it just needs to preserve the real Docker runtime contract and be covered by CI before other Java services copy it.

Addresses both review comments.

Contract test: the image's runtime contract was only verified by hand, and
every part of it fails at container startup rather than at build time. The
test asserts the jar is at /app.jar (the path the entrypoint names), the base
image's shelless_ulimit shim survives (oci_image replaces rather than appends
the base entrypoint), and java is invoked via the arch-stable /usr/bin/java
rather than a per-arch JAVA_HOME path.

Unlike the Go services' image_entrypoint_mode_test it does not assert an exec
bit: a jar is read by the JVM, never executed, so mode 0644 is correct.

sh_test is loaded explicitly from @rules_shell. The Go subtrees rely on it
being a built-in global, which works only because they are still on Bazel 8;
cloud-tasks builds in the root module on Bazel 9, where that global is gone.

java_bin: the base attribute documented "a JRE on PATH" while the generated
entrypoint hardcoded /usr/bin/java, so a compliant custom base with Java
elsewhere would have failed at startup. The path is now a configurable
attribute defaulting to /usr/bin/java, and the base contract states the
requirement precisely.

Verified: test passes against the real image, and fails when given a wrong
expected entrypoint (negative control), so it is not vacuous.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Line 34: Update the tar-listing checks around the candidate and extracted
image validations in image_contract_test.sh to avoid grep’s quiet early exit
under pipefail. Replace the grep -q-style matching with a full-stream-consuming
approach while preserving the existing regular expression and valid-image
acceptance behavior.
- Around line 30-44: The image contract test must validate the final merged
filesystem rather than merely finding app.jar in any individual layer. Replace
the per-layer jar_found check with ordered layer application into an assembled
root filesystem, honoring whiteout/deletion semantics, then assert that the
resulting root contains /app.jar.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c68995a3-b7c3-4b44-8ce7-f4a6f08c11c5

📥 Commits

Reviewing files that changed from the base of the PR and between 8e3ca95 and a0aab8e.

⛔ Files ignored due to path filters (1)
  • MODULE.bazel.lock is excluded by !**/*.lock, !**/MODULE.bazel.lock
📒 Files selected for processing (3)
  • rules/oci/private/java.bzl
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
  • src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • rules/oci/private/java.bzl
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel

Comment thread src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh Outdated
Addresses the blocking and important findings from review. The image did not
reproduce the runtime the pre-Bazel nvct-service/Dockerfile establishes.

Runtime parity. The Dockerfile sets ULIMIT_FLAG=1, JDK_JAVA_OPTIONS, WORKDIR
/home/app, and installs the jar at /usr/share/app.jar. None of that was
carried over. ULIMIT_FLAG is load-bearing: the base's shelless_ulimit shim
reads it, so preserving the shim in the entrypoint while dropping the variable
meant the shim ran and raised nothing. JDK_JAVA_OPTIONS carries the container
heap sizing (MaxRAMPercentage=40.0), so losing it silently changes JVM memory
behavior in production. create_oci_image and java_oci_image now take env and
workdir, both optional and defaulting to leaving the base untouched, so
go_oci_image callers are unaffected.

jar_path now controls where the jar is installed. pkg_tar preserves the source
basename, so jar_path only renamed the entrypoint: a jar not already called
app.jar, or a jar_path pointing elsewhere, produced an image whose entrypoint
referenced a nonexistent file. pkg_files renames and relocates it, so the layer
and the entrypoint are derived from one value and cannot disagree. This is what
made /usr/share/app.jar expressible at all.

Test coverage extended to the full contract: jar location, entrypoint,
ULIMIT_FLAG, MaxRAMPercentage, and WorkingDir.

Verified on the built index, both amd64 and arm64:
  Entrypoint /usr/bin/shelless_ulimit /usr/bin/java -jar /usr/share/app.jar
  WorkingDir /home/app
  ULIMIT_FLAG=1 and JDK_JAVA_OPTIONS present

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda

Copy link
Copy Markdown
Contributor Author

@sanjay-saxena thanks, and thanks to Codex. Findings 1-3 were all real; 1 and 2 were genuine bugs, not just doc gaps. Fixed in 343d5d5.

1. Docker runtime parity (blocking). You were right, and it was worse than the finding states: the Dockerfile also installs the jar at /usr/share/app.jar, while the image put it at /app.jar. So the image diverged on four things, not three.

ULIMIT_FLAG was the significant one. Preserving the shelless_ulimit shim in the entrypoint while dropping the variable the shim reads meant the shim ran and raised nothing, which looks correct in the manifest and does nothing at runtime. Losing JDK_JAVA_OPTIONS was arguably worse, since MaxRAMPercentage=40.0 governs container heap sizing.

create_oci_image and java_oci_image now take env and workdir. Both are optional and default to leaving the base image's values untouched, so go_oci_image callers are unaffected. cloud-tasks supplies ULIMIT_FLAG=1, the full JDK_JAVA_OPTIONS, and workdir = "/home/app".

2. jar_path did not control installation (important). Correct, and this is what made /usr/share/app.jar impossible to express. pkg_tar preserves the source basename, so jar_path only renamed the entrypoint; it worked solely because spring_boot_app happens to emit app.jar. Switched to pkg_files with renames and prefix, so the layer path and the entrypoint derive from one value and cannot disagree. Kept it configurable rather than hardcoding, now that it is real.

3. CI is not testing the image (important). Partly addressed, and I want to be precise about what is and is not covered. :image_contract_test is not manual-tagged, so a wildcard bazel test //... selects it, and building it forces the image to build. It asserts the jar is at /usr/share/app.jar, the entrypoint, ULIMIT_FLAG, MaxRAMPercentage, and WorkingDir.

Not covered, and I did not claim otherwise: multi-arch manifest assertions and actually starting the container. The test inspects the host-arch tarball. I verified both architectures by hand on the built index:

--- amd64 ---   --- arm64 ---
Entrypoint: [/usr/bin/shelless_ulimit, /usr/bin/java, -jar, /usr/share/app.jar]
WorkingDir: /home/app
ULIMIT_FLAG=1
JDK_JAVA_OPTIONS=-XX:MaxRAMPercentage=40.0 ...

Automating the arch and start-up checks needs a Docker-capable lane, which cloud-tasks already has (ci_lane: docker-host). Happy to add it here if you want it before merge, or as the follow-up.

4. Scope. Agreed, and I will retitle to "Java OCI build support". It creates the _index artifact release tooling needs; publishing is not wired and is proven by the follow-up.

5. Custom base contract (minor). Fixed earlier in a0aab8e: java_bin is a configurable attribute defaulting to /usr/bin/java, and the base doc now states the exact requirement. Deliberately not JAVA_HOME-derived, since the distroless bases set that per-architecture and it would break the arm64 half of the index.

The test also has a negative control: it fails when given a wrong expected entrypoint, so it is not vacuous.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel`:
- Around line 63-72: Restore the `nvct_library_test(name = "tests")` target in
the BUILD file alongside the existing `sh_test`; retain the image-contract test
and ensure the standard test invocation runs the service’s Java test suite as
well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b4303be7-46ba-4ccf-beb0-f1eaf2dfb468

📥 Commits

Reviewing files that changed from the base of the PR and between a0aab8e and 343d5d5.

📒 Files selected for processing (4)
  • rules/oci/private/common.bzl
  • rules/oci/private/java.bzl
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
  • src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • rules/oci/private/java.bzl

Comment thread src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
Three review findings.

Restore nvct_library_test(name = "tests"). An earlier edit to this BUILD file
replaced a span of text rather than inserting, and silently removed the Java
test suite along with it. The image contract test does not substitute for it;
both targets belong here. Re-added the image targets additively so this cannot
recur.

Avoid grep -q inside a pipeline. Under pipefail, grep -q closes the pipe on the
first match, the producer takes SIGPIPE, and the pipeline reports failure, so a
valid image could be rejected. The pipeline cases now consume the full stream.
The remaining grep -q calls read a file directly rather than a pipe, so they
carry no SIGPIPE risk and are unchanged.

Check whiteouts. Finding the jar in some layer is necessary but not
sufficient: a later layer can delete it with an OCI whiteout marker, leaving
the merged filesystem without it. The test now also asserts no layer whites out
the jar, its parent directories, or the tree via an opaque marker.

Verified: contract test passes, and //...:tests resolves again.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh (1)

86-90: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert exact environment entries and the complete JVM option value.

Unanchored matching allows unrelated fields such as NOT_ULIMIT_FLAG=1 to satisfy the check. It also validates only the MaxRAMPercentage substring, so the other required JDK_JAVA_OPTIONS values configured in src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel could be dropped without failing this test. Compare exact Env entries after selecting the config blob.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`
around lines 86 - 90, Update the metadata validation loop in
image_contract_test.sh to select the config blob, extract its Env entries, and
compare complete entries exactly rather than using unanchored substring
matching. Validate the full JDK_JAVA_OPTIONS value, including all required JVM
options from the service configuration, while retaining the existing checks for
ULIMIT_FLAG=1 and the WorkingDir field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Around line 53-55: Update the tar-entry regex in the image contract test so
the opaque-whiteout pattern matches only the root, /usr, and /usr/share ancestor
directories of the jar. Preserve the existing matching behavior for the other
whiteout entries and prevent opaque whiteouts in unrelated directories from
failing the test.
- Around line 67-70: Update the image contract test loops around the Entrypoint
and Env assertions to resolve the image manifest or index to its config digest,
then inspect only that OCI config blob. Stop scanning every regular file in the
extracted archive, including layer tars or bundled payloads, while preserving
the existing expected Entrypoint and Env validations against the resolved
config.

---

Outside diff comments:
In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`:
- Around line 86-90: Update the metadata validation loop in
image_contract_test.sh to select the config blob, extract its Env entries, and
compare complete entries exactly rather than using unanchored substring
matching. Validate the full JDK_JAVA_OPTIONS value, including all required JVM
options from the service configuration, while retaining the existing checks for
ULIMIT_FLAG=1 and the WorkingDir field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bb0a5ee2-97e2-4852-a8aa-dbef6900049f

📥 Commits

Reviewing files that changed from the base of the PR and between 343d5d5 and 17c22dd.

📒 Files selected for processing (2)
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel
  • src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/control-plane-services/cloud-tasks/nvct-service/BUILD.bazel

Comment on lines +53 to +55
if tar -tf "${candidate}" \
| grep -E '^(\./)?usr/share/\.wh\.app\.jar$|^(\./)?usr/\.wh\.share$|^(\./)?\.wh\.usr$|\.wh\.\.wh\.opq$' \
>/dev/null; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf '--- file outline ---\n'
ast-grep outline src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh || true

printf '\n--- target excerpt ---\n'
cat -n src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,140p'

printf '\n--- search whiteout-related patterns ---\n'
rg -n "\.wh\.\.wh\.\.opq|\.wh\.app\.jar|\.wh\.share|\.wh\.usr|whiteout" src/control-plane-services/cloud-tasks/nvct-service -S

Repository: NVIDIA/nvcf

Length of output: 240


🏁 Script executed:

set -euo pipefail

echo '--- file outline ---'
ast-grep outline src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh || true

echo
echo '--- target excerpt ---'
cat -n src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,140p'

echo
echo '--- search whiteout-related patterns ---'
rg -n "\.wh\.\.wh\.\.opq|\.wh\.app\.jar|\.wh\.share|\.wh\.usr|whiteout" src/control-plane-services/cloud-tasks/nvct-service -S

Repository: NVIDIA/nvcf

Length of output: 5381


Scope the opaque-whiteout match to the jar’s ancestor directories. The final \.wh\.\.wh\.\.opq alternative matches opaque whiteouts anywhere in any layer, so an unrelated directory can fail this contract test. Restrict it to /, /usr, and /usr/share only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`
around lines 53 - 55, Update the tar-entry regex in the image contract test so
the opaque-whiteout pattern matches only the root, /usr, and /usr/share ancestor
directories of the jar. Preserve the existing matching behavior for the other
whiteout entries and prevent opaque whiteouts in unrelated directories from
failing the test.

Comment on lines +67 to +70
while IFS= read -r meta; do
grep -q '"Entrypoint"' "${meta}" 2>/dev/null || continue
if tr -d ' \n' < "${meta}" \
| grep '"Entrypoint":\["/usr/bin/shelless_ulimit","/usr/bin/java","-jar","/usr/share/app.jar"\]' >/dev/null; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## repo files"
git ls-files | sed -n '1,200p'

echo
echo "## AGENTS files"
fd -a 'AGENTS.md' .

echo
echo "## target file around relevant lines"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'

Repository: NVIDIA/nvcf

Length of output: 13088


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## target file around relevant lines"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'

echo
echo "## search for manifest/config handling in related files"
rg -n "manifest|config digest|Entrypoint|Env|image_contract_test" src/control-plane-services/cloud-tasks/nvct-service -S

Repository: NVIDIA/nvcf

Length of output: 226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## AGENTS files"
fd -a 'AGENTS.md' .

echo
echo "## target file"
nl -ba src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh | sed -n '1,220p'

echo
echo "## related references"
rg -n "manifest|config digest|Entrypoint|Env|image_contract_test" src/control-plane-services/cloud-tasks/nvct-service -S

Repository: NVIDIA/nvcf

Length of output: 2126


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh

echo "## exists?"
test -f "$file" && echo yes || echo no

echo
echo "## relevant excerpt"
sed -n '55,110p' "$file" | nl -ba

echo
echo "## show any manifest/index parsing in this script"
grep -nE 'manifest|index|config|digest|Entrypoint|Env' "$file"

Repository: NVIDIA/nvcf

Length of output: 226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## subtree guidance"
sed -n '1,220p' src/control-plane-services/cloud-tasks/AGENTS.md

echo
echo "## image_contract_test.sh excerpt"
sed -n '1,220p' src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh

echo
echo "## references in the script"
grep -nE 'manifest|index|config|digest|Entrypoint|Env|tar|oci|archive|blob' src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh

Repository: NVIDIA/nvcf

Length of output: 7559


Inspect the OCI config blob directly
These loops read every regular file under the extracted archive, so a layer tar or bundled payload containing the expected Entrypoint/Env text can make the test pass even if the actual image config is wrong. Resolve manifest.json/index to the config digest and assert against that blob only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/control-plane-services/cloud-tasks/nvct-service/image_contract_test.sh`
around lines 67 - 70, Update the image contract test loops around the Entrypoint
and Env assertions to resolve the image manifest or index to its config digest,
then inspect only that OCI config blob. Stop scanning every regular file in the
extracted archive, including layer tars or bundled payloads, while preserving
the existing expected Entrypoint and Env validations against the resolved
config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants