Skip to content

fix(function-autoscaler): build linux/arm64 images - #533

Merged
balajinvda merged 4 commits into
mainfrom
fa-arm64-simple
Jul 29, 2026
Merged

fix(function-autoscaler): build linux/arm64 images#533
balajinvda merged 4 commits into
mainfrom
fa-arm64-simple

Conversation

@balajinvda

@balajinvda balajinvda commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Why

Closes #527.

function-autoscaler is the only one of nineteen Bazel modules publishing a
single-architecture image index. Every other service already ships amd64 and
arm64.

The service was deliberately pinned to amd64 with a code comment blaming
openssl-sys. That comment was accurate about the symptom but the cause was
three separate defects stacked on top of each other, and fixing any one alone
still fails the build. That is why earlier attempts kept concluding the problem
was somewhere else.

What changed

  1. oci.pull named the platform linux/arm64, but nvcr.io/nvidia/distroless/cc
    publishes its aarch64 manifest under the v8 variant and rules_oci matches the
    platform string exactly, so the pull failed outright. http-invocation pins the
    same image and the same digest as linux/arm64/v8 and has always built both
    architectures. This defect alone blocks the build regardless of openssl.

  2. openssl-sys checks <TARGET>_OPENSSL_LIB_DIR before the unprefixed name. Only
    the unprefixed form was set, so an aarch64 build silently resolved to the
    x86_64 library path rather than failing loudly. Explicit per-target entries
    replace that fallback.

  3. libssl-dev is Multi-Arch: same and splits its headers: 142
    arch-independent headers are shared at /usr/include/openssl/, while
    opensslconf.h and configuration.h are per-arch under
    /usr/include/<triplet>/openssl/. Host gcc searches its own triplet directory
    implicitly, which is why amd64 never needed this; the zig toolchain used for
    arm64 is hermetic and searches neither. OPENSSL_INCLUDE_DIR holds a single
    directory and cannot express the pair, so the triplet path is supplied through
    the cc crate's target-suffixed CFLAGS.

DEFAULT_PLATFORMS in the service's forked rules/oci regains linux_arm64.

Alternatives ruled out, with evidence

Vendored OpenSSL: openssl-src's bundled source tree is not materialised in the
crate_universe sandbox. The build fails with
openssl-src-.../openssl: No such file or directory, with and without a
build_script_data annotation. An earlier test that appeared to succeed was
invalid because OPENSSL_NO_VENDOR=1 was still set.

Porting Cassandra TLS from openssl to rustls (#529): works, but it is a change
to production TLS code to solve a build-configuration problem, and it would have
been the wrong trade here. Superseded by this PR.

Replacing zig with crossbuild-essential-arm64: its default include path covers
the triplet directory, so defect 3 would resolve itself. Rejected because zig is
a Bazel-fetched dependency that installs nothing in the CI image, whereas the
cross compiler is a real image install and would need a hand-written
cc_toolchain_config (the repo has none), diverging this module from the
eighteen others that all register zig.

Customer Release Notes

function-autoscaler container images are now published for linux/arm64 in
addition to linux/amd64.

Plan Summary

Not applicable.

Usage

Not applicable.

Testing

Not verifiable on the current CI image, which is why this is a draft. The build
needs libssl-dev:arm64, added in bazel-ci 0.14.0.

Two package properties were verified directly against the Ubuntu arm64 debs,
since both are load-bearing and neither is obvious:

  • readelf on aarch64 libssl.so.3 shows a maximum requirement of GLIBC_2.38,
    matching the registered linux_arm64_gnu.2.38 toolchain. No symbol-version
    wall at link time.
  • The libssl-dev arm64 deb contains exactly two files under the triplet
    include directory and 142 under the shared one, which is what defect 3 above
    is about.

Notes

Draft until bazel-ci 0.14.0 is published and mirrored to ghcr. The second commit
bumps the three fallback pins; merging before the tag exists fails every matrix
job at container pull.

Nothing enforces that a forked rules/oci lists both platforms, so this class
of regression can recur silently in another module. Worth a follow-up guard.

References

Closes #527.

Related Merge Requests/Pull Requests

Requires the bazel-ci image MR adding arm64 libssl. Supersedes #529.

Dependencies

None.

Summary by CodeRabbit

  • New Features

    • Expanded multi-architecture container publishing to include Linux ARM64 alongside Linux AMD64 for function autoscaler images.
    • Added stricter platform coverage validation to confirm the published image index contains exactly the expected architectures.
  • Bug Fixes

    • Improved cross-platform OpenSSL handling to provide correct per-target include/library paths and compiler flags for more reliable AMD64/ARM64 builds.
  • Chores

    • Updated Bazel-based CI and manual publish workflows to use the newer pinned Bazel CI container image.
    • Added/expanded OCI image index platform tests to cover both flat and nested layouts, including failure scenarios.

balajinvda and others added 2 commits July 29, 2026 11:22
function-autoscaler was the only one of nineteen Bazel modules producing a
single-architecture image index. Three independent defects had to be fixed
together; each one alone still fails the build.

The oci.pull platform list named "linux/arm64", but nvcr.io/nvidia/distroless/cc
publishes its aarch64 manifest under the v8 variant and rules_oci matches the
platform string exactly, so the pull failed outright. http-invocation pins the
same image and digest as arm64/v8 and has always built both architectures.

openssl-sys checks <TARGET>_OPENSSL_LIB_DIR before the unprefixed name. Only the
unprefixed form was set, so an aarch64 build silently resolved to the x86_64
library path. Explicit per-target entries replace that fallback.

libssl-dev is Multi-Arch: same and splits its headers: the arch-independent
ones are shared under /usr/include/openssl/ while opensslconf.h and
configuration.h are per-arch under /usr/include/<triplet>/openssl/. Host gcc
searches its own triplet directory implicitly, which is why amd64 never needed
this, but the zig toolchain used for arm64 is hermetic and searches neither.
OPENSSL_INCLUDE_DIR holds a single directory, so the triplet path is supplied
through the cc crate's target-suffixed CFLAGS.

Vendored OpenSSL is not an alternative: openssl-src's bundled source tree is
not materialised in the crate_universe sandbox and the build fails with
"openssl-src-.../openssl: No such file or directory", with or without a
build_script_data annotation.

Requires a bazel-ci image carrying libssl-dev:arm64. Ubuntu's aarch64
libssl.so.3 requires at most GLIBC_2.38, which the registered
linux_arm64_gnu.2.38 zig toolchain provides.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.14.0 adds libssl-dev:arm64, which the function-autoscaler arm64 build in the
preceding commit needs in order to resolve aarch64 openssl. These are the
fallback values behind the BAZEL_CI_IMAGE repository variable; when that
variable is set it takes precedence and must be bumped alongside.

Merge only after the image is published and mirrored to ghcr. Pinning a tag
that does not exist yet fails every job in the matrix at container pull, before
any step runs.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request updates default Bazel CI images to 0.14.0 and extends function-autoscaler build and OCI configuration to support linux/arm64 alongside linux/amd64. It adds OCI image-index architecture validation with nested and flat layout regression tests.

Changes

CI image updates

Layer / File(s) Summary
Update workflow container defaults
.github/workflows/bazel.yml, .github/workflows/chart-push-manual.yml, .github/workflows/image-push-manual.yml
Default ghcr.io/nvidia/nvcf/bazel-ci fallbacks change from 0.13.0 to 0.14.0, while variable overrides remain supported.

Function autoscaler arm64 support

Layer / File(s) Summary
Configure arm64 builds and images
src/control-plane-services/function-autoscaler/MODULE.bazel, src/control-plane-services/function-autoscaler/rules/oci/private/common.bzl
Adds target-specific OpenSSL settings, selects the arm64/v8 distroless repository, adds rules_shell, and includes linux/arm64 in default OCI platforms.
Add OCI platform coverage guard
src/control-plane-services/function-autoscaler/rules/oci/private/BUILD.bazel, src/control-plane-services/function-autoscaler/rules/oci/private/common.bzl, src/control-plane-services/function-autoscaler/rules/oci/private/image_index_platforms_test.sh
Adds a generated shell test that resolves OCI index, manifest, and config blobs and validates the required architecture set.
Test OCI platform validation
src/control-plane-services/function-autoscaler/rules/oci/private/test_image_index_platforms.sh
Exercises nested and flat layouts, including missing architectures, mismatched configs, unexpected architectures, invalid layouts, and missing blobs.

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

Sequence Diagram(s)

sequenceDiagram
  participant create_oci_image
  participant image_index_platforms_test.sh
  participant OCIImageIndex
  create_oci_image->>image_index_platforms_test.sh: Pass generated OCI layout and required architectures
  image_index_platforms_test.sh->>OCIImageIndex: Resolve index, manifest, and config blobs
  OCIImageIndex-->>image_index_platforms_test.sh: Return declared architectures
  image_index_platforms_test.sh-->>create_oci_image: Report platform coverage result
Loading

Possibly related PRs

Suggested reviewers: borao, kristinapathak, sbaum1994

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses valid Conventional Commits format and accurately describes the main ARM64 image build fix.
Linked Issues check ✅ Passed The changes address #527 by enabling function-autoscaler ARM64 image publication while keeping AMD64 support intact.
Out of Scope Changes check ✅ Passed The workflow pins, Bazel rule updates, and tests all support the ARM64 image publishing goal and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 fa-arm64-simple

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

@balajinvda
balajinvda marked this pull request as ready for review July 29, 2026 19:13
@balajinvda
balajinvda requested review from a team as code owners July 29, 2026 19:13

@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/function-autoscaler/MODULE.bazel`:
- Around line 113-152: Add repository-native regression coverage for the
autoscaler configuration around crate.annotation, including a CI build targeting
//platforms:linux_arm64 and verification that the published image index contains
both AMD64 and ARM64 variants. If such tests cannot be added, document the
omission and rationale in the pull request description.
🪄 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: afadc083-d2a7-4f95-83c5-d2fcd85daf0e

📥 Commits

Reviewing files that changed from the base of the PR and between ef6f96c and cf418c4.

📒 Files selected for processing (5)
  • .github/workflows/bazel.yml
  • .github/workflows/chart-push-manual.yml
  • .github/workflows/image-push-manual.yml
  • src/control-plane-services/function-autoscaler/MODULE.bazel
  • src/control-plane-services/function-autoscaler/rules/oci/private/common.bzl

Comment thread src/control-plane-services/function-autoscaler/MODULE.bazel
@balajinvda
balajinvda requested review from borao and sbaum1994 and removed request for Max-NV and nvaghela-oss July 29, 2026 19:34
This service published an amd64-only image index for months and the build
stayed green throughout. Dropping a platform is a configuration edit, not a
compile error: DEFAULT_PLATFORMS lost an entry and every downstream target
happily built the smaller index. Only someone reading the registry would have
noticed.

create_oci_image now generates a platform-coverage test alongside every image.
It walks the OCI layout the same way the Java contract test does
(index.json -> manifest list -> per-platform manifest -> config) and checks both
directions: a required architecture missing, and an architecture present that
nobody declared. Following the descriptor through to the config blob also
catches an entry filed under one platform while pointing at an image built for
another, which pushes without complaint and then runs the wrong binary.

The expected architectures are a separate REQUIRED_ARCHES constant rather than
something derived from DEFAULT_PLATFORMS. Deriving them would make the test
restate whatever the build already decided, so removing arm64 would also remove
it from the expectation and the test would pass. That is precisely the
regression being guarded against.

The generated test is deliberately not tagged manual. The image targets are, so
inheriting their tags would keep it out of `bazel test //...` and leave the
guard inert.

image_index_platforms_selftest exercises the guard against synthetic layouts,
because the guard itself only ever sees well-formed indexes in a healthy build
and nothing else would catch it passing when it should fail. Writing it was
worthwhile: it caught the guard reporting success on an index whose arm64
manifest blob was missing.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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: 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/function-autoscaler/rules/oci/private/test_image_index_platforms.sh`:
- Around line 28-53: Extend make_layout to support a flat OCI index case where
index.json contains the manifests array directly and no separate list blob is
created. Add a self-test fixture invoking this mode so
image_index_platforms_test.sh exercises both the existing nested layout and the
spec-standard flat layout.
🪄 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: 3ef9b5ba-f49f-4283-b209-b5ed158d6b0a

📥 Commits

Reviewing files that changed from the base of the PR and between cf418c4 and 8de70b8.

📒 Files selected for processing (5)
  • src/control-plane-services/function-autoscaler/MODULE.bazel
  • src/control-plane-services/function-autoscaler/rules/oci/private/BUILD.bazel
  • src/control-plane-services/function-autoscaler/rules/oci/private/common.bzl
  • src/control-plane-services/function-autoscaler/rules/oci/private/image_index_platforms_test.sh
  • src/control-plane-services/function-autoscaler/rules/oci/private/test_image_index_platforms.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/control-plane-services/function-autoscaler/MODULE.bazel

@balajinvda
balajinvda enabled auto-merge July 29, 2026 20:03
…orm guard

The guard walked index.json -> nested index blob -> manifest -> config, and the
self-test built fixtures in exactly that shape. Both encoded the same
assumption, so the self-test could not have caught the assumption being wrong.

That shape is what rules_oci emits today: oci/private/image_index.sh.tpl sets
index.json's .manifests to a single application/vnd.oci.image.index.v1+json
descriptor, putting the platform descriptors one level down. The OCI spec also
permits them inline in index.json, and other tooling emits that flatter form.

Rather than depend on which shape the ruleset happens to produce, the guard now
detects it: platform architectures present in index.json means the descriptors
are inline, otherwise it follows the single descriptor to the nested blob. This
matters because the failure mode is quiet. Against an unexpected layout the walk
finds no architectures at all, which reads as a missing platform rather than as
a guard that no longer works, and the obvious "fix" for that noise would be to
stop failing on an empty result.

The self-test now covers the flat form independently, including the
missing-arm64 and mislabelled-config cases, so neither layout is validated only
by a fixture that shares its assumptions.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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: 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/function-autoscaler/rules/oci/private/image_index_platforms_test.sh`:
- Around line 59-65: The JSON normalization in image_index_platforms_test.sh
must remove tabs and carriage returns as well as spaces and newlines before the
architecture guard matches. In
src/control-plane-services/function-autoscaler/rules/oci/private/test_image_index_platforms.sh
lines 50-54, add a fixture mode producing valid tab- or CRLF-formatted flat
JSON; in lines 133-152, invoke the guard with that formatted fixture to cover
the normalization path.
🪄 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: 9e14d9f3-a418-4e53-890c-f24e2fe3df36

📥 Commits

Reviewing files that changed from the base of the PR and between 8de70b8 and 0d5f432.

📒 Files selected for processing (2)
  • src/control-plane-services/function-autoscaler/rules/oci/private/image_index_platforms_test.sh
  • src/control-plane-services/function-autoscaler/rules/oci/private/test_image_index_platforms.sh

@balajinvda
balajinvda added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit daa1c83 Jul 29, 2026
38 checks passed
@balajinvda
balajinvda deleted the fa-arm64-simple branch July 29, 2026 23:09
@balajinvda

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version nvcf-function-autoscaler-v1.18.10 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(autoscaler): publish arm64 image for control observability installs

3 participants