From 35923931483cab2d5f2c88ab2fe24e466c15b652 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Wed, 16 Sep 2026 16:35:51 -0700 Subject: [PATCH 01/16] feat: Add ARM64 (arm64) stemcell build support Enable building Ubuntu Noble ARM64 stemcells for Cloud Foundry on AWS Graviton and other ARM64 processors. Architecture is threaded through as an optional parameter defaulting to amd64, so existing x86_64 stemcell builds are byte-identical and unaffected. Ruby orchestration: - Definition carries an architecture (default amd64); the arch is appended to the stemcell name only for non-amd64 builds, and included in equality - BuilderOptions emits a stemcell_arch setting - StemcellPackager maps the arch to the manifest field (amd64->x86_64, arm64->aarch64) - The build_os_image and build rake tasks accept an optional architecture argument (defaults to amd64) Build stages (arch-conditional, amd64 unchanged): - A stemcell_target_arch helper derives the target arch from the bootstrapped rootfs (dpkg --print-architecture), which is reliably available to every stage regardless of settings propagation - base_debootstrap: debootstrap --arch honors the target arch and defaults arm64 to the ports.ubuntu.com mirror (archive.ubuntu.com has no arm64) - base_apt: writes ports.ubuntu.com sources for arm64 - system_grub: installs grub-efi-arm64 (arm64 has no "grub2" metapackage) - image_install_grub: uses --target=arm64-efi and skips the i386-pc BIOS fallback on arm64 (ARM64 is UEFI-only) - password_policies: uses the aarch64-linux-gnu multiarch triplet CI: - Add a STEMCELL_ARCH param (default amd64) to the os-image and stemcell build tasks and thread it into the rake invocations Tests: - Unit specs cover the arch parameter, name suffix, and manifest mapping - os_image spec is made arch-aware for the apt mirror, grub package, gfxblacklist.txt, and PAM multiarch path Note: installing the ARM64 BOSH agent into the stemcell (bosh_go_agent metalink) requires a published linux-arm64 agent artifact and is a follow-up once the agent's ARM64 build is released. A dedicated ARM64 pipeline job is deferred until ARM64 CI workers are available. Part of RFC #1530 (ARM64 architecture support for Cloud Foundry). Validated on AWS Graviton (t4g): the ARM64 Ubuntu Noble OS image builds end-to-end and passes the full os_image spec suite (376 examples, 0 failures); Ruby unit specs pass (57 examples, 0 failures). --- Rakefile | 12 ++-- .../lib/bosh/stemcell/builder_options.rb | 1 + bosh-stemcell/lib/bosh/stemcell/definition.rb | 19 ++++-- .../lib/bosh/stemcell/stemcell_packager.rb | 4 +- .../bosh/stemcell/builder_options_spec.rb | 8 ++- .../spec/bosh/stemcell/definition_spec.rb | 62 ++++++++++++++++++- bosh-stemcell/spec/os_image/ubuntu_spec.rb | 28 ++++++--- ci/tasks/build.sh | 6 +- ci/tasks/build.yml | 1 + ci/tasks/os-images/build.sh | 6 +- ci/tasks/os-images/build.yml | 1 + stemcell_builder/lib/helpers.sh | 21 +++++++ stemcell_builder/stages/base_apt/apply.sh | 19 +++++- .../stages/base_debootstrap/apply.sh | 10 ++- .../stages/base_debootstrap/config.sh | 5 +- .../stages/image_install_grub/apply.sh | 9 ++- .../stages/password_policies/apply.sh | 14 +++-- stemcell_builder/stages/system_grub/apply.sh | 9 ++- 18 files changed, 200 insertions(+), 35 deletions(-) diff --git a/Rakefile b/Rakefile index 61e3f80865..3670300c36 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require "standard/rake" namespace :stemcell do desc "Build a base OS image for use in stemcells" - task :build_os_image, [:operating_system_name, :operating_system_version, :os_image_path] do |_, args| + task :build_os_image, [:operating_system_name, :operating_system_version, :os_image_path, :architecture] do |_, args| require "bosh/stemcell/archive_handler" require "bosh/stemcell/build_environment" require "bosh/stemcell/definition" @@ -11,7 +11,9 @@ namespace :stemcell do require "bosh/stemcell/stage_collection" require "bosh/stemcell/stage_runner" - definition = Bosh::Stemcell::Definition.for("null", "null", args.operating_system_name, args.operating_system_version) + args.with_defaults(architecture: Bosh::Stemcell::Definition::DEFAULT_ARCHITECTURE) + + definition = Bosh::Stemcell::Definition.for("null", "null", args.operating_system_name, args.operating_system_version, args.architecture) environment = Bosh::Stemcell::BuildEnvironment.new( ENV.to_hash, definition, @@ -42,7 +44,7 @@ namespace :stemcell do end desc "Build a stemcell, requires `os_image_path` pointing at an image created via `stemcell:build_os_image`" - task :build, [:infrastructure_name, :hypervisor_name, :operating_system_name, :operating_system_version, :os_image_path, :build_number] do |_, args| + task :build, [:infrastructure_name, :hypervisor_name, :operating_system_name, :operating_system_version, :os_image_path, :build_number, :architecture] do |_, args| require "bosh/stemcell/build_environment" require "bosh/stemcell/definition" require "bosh/stemcell/stage_collection" @@ -50,9 +52,9 @@ namespace :stemcell do require "bosh/stemcell/stemcell_packager" require "bosh/stemcell/stemcell_builder" - args.with_defaults(build_number: "0000") + args.with_defaults(build_number: "0000", architecture: Bosh::Stemcell::Definition::DEFAULT_ARCHITECTURE) - definition = Bosh::Stemcell::Definition.for(args.infrastructure_name, args.hypervisor_name, args.operating_system_name, args.operating_system_version) + definition = Bosh::Stemcell::Definition.for(args.infrastructure_name, args.hypervisor_name, args.operating_system_name, args.operating_system_version, args.architecture) environment = Bosh::Stemcell::BuildEnvironment.new( ENV.to_hash, definition, diff --git a/bosh-stemcell/lib/bosh/stemcell/builder_options.rb b/bosh-stemcell/lib/bosh/stemcell/builder_options.rb index 9144fbbefc..15e668dc3a 100644 --- a/bosh-stemcell/lib/bosh/stemcell/builder_options.rb +++ b/bosh-stemcell/lib/bosh/stemcell/builder_options.rb @@ -24,6 +24,7 @@ def default "stemcell_operating_system" => operating_system.name, "stemcell_operating_system_version" => operating_system.version, "stemcell_operating_system_variant" => operating_system.variant, + "stemcell_arch" => definition.architecture, "ruby_bin" => ruby_bin, "image_create_disk_size" => image_create_disk_size, "os_image_tgz" => os_image_tgz_path diff --git a/bosh-stemcell/lib/bosh/stemcell/definition.rb b/bosh-stemcell/lib/bosh/stemcell/definition.rb index ac6fabd5a8..3dbd25f77e 100644 --- a/bosh-stemcell/lib/bosh/stemcell/definition.rb +++ b/bosh-stemcell/lib/bosh/stemcell/definition.rb @@ -3,20 +3,25 @@ module Bosh::Stemcell class Definition - attr_reader :infrastructure, :hypervisor_name, :operating_system + # Architecture defaults to amd64 so existing stemcell builds are unaffected. + DEFAULT_ARCHITECTURE = "amd64".freeze - def self.for(infrastructure_name, hypervisor_name, operating_system_name, operating_system_version) + attr_reader :infrastructure, :hypervisor_name, :operating_system, :architecture + + def self.for(infrastructure_name, hypervisor_name, operating_system_name, operating_system_version, architecture = DEFAULT_ARCHITECTURE) new( Bosh::Stemcell::Infrastructure.for(infrastructure_name), hypervisor_name, - Bosh::Stemcell::OperatingSystem.for(operating_system_name, operating_system_version) + Bosh::Stemcell::OperatingSystem.for(operating_system_name, operating_system_version), + architecture ) end - def initialize(infrastructure, hypervisor_name, operating_system) + def initialize(infrastructure, hypervisor_name, operating_system, architecture = DEFAULT_ARCHITECTURE) @infrastructure = infrastructure @hypervisor_name = hypervisor_name @operating_system = operating_system + @architecture = architecture || DEFAULT_ARCHITECTURE end def stemcell_name(disk_format) @@ -28,6 +33,9 @@ def stemcell_name(disk_format) stemcell_name_parts << operating_system.version if operating_system.version stemcell_name_parts << operating_system.variant if operating_system.variant stemcell_name_parts << disk_format unless disk_format == infrastructure.default_disk_format + # Only embed the architecture in the name for non-default (arm64) builds so + # existing amd64 stemcell names remain byte-identical. + stemcell_name_parts << architecture unless architecture == DEFAULT_ARCHITECTURE stemcell_name_parts.join("-") end @@ -38,7 +46,8 @@ def disk_formats def ==(other) infrastructure == other.infrastructure && - operating_system == other.operating_system + operating_system == other.operating_system && + architecture == other.architecture end end end diff --git a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb index 2e339caf77..e068865471 100644 --- a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb +++ b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb @@ -49,7 +49,9 @@ def manifest(disk_format) end def manifest_cloud_properties(disk_format, infrastructure, stemcell_name) - architecture = "x86_64" + # Map the BOSH/Debian architecture to the kernel architecture string + # recorded in the manifest. Defaults to x86_64 for amd64 builds. + architecture = {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(definition.architecture, "x86_64") { "name" => stemcell_name, diff --git a/bosh-stemcell/spec/bosh/stemcell/builder_options_spec.rb b/bosh-stemcell/spec/bosh/stemcell/builder_options_spec.rb index b76cdbe0ed..3b010dbda7 100644 --- a/bosh-stemcell/spec/bosh/stemcell/builder_options_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/builder_options_spec.rb @@ -20,7 +20,8 @@ module Bosh::Stemcell instance_double( "Bosh::Stemcell::Definition", infrastructure: infrastructure, - operating_system: operating_system + operating_system: operating_system, + architecture: "amd64" ) } @@ -58,6 +59,11 @@ module Bosh::Stemcell expect(result["stemcell_operating_system_variant"]).to eq("bear") end + it "sets the stemcell architecture" do + result = stemcell_builder_options.default + expect(result["stemcell_arch"]).to eq("amd64") + end + # rubocop:disable Metrics/MethodLength def self.it_sets_correct_environment_variables describe "setting enviroment variables" do diff --git a/bosh-stemcell/spec/bosh/stemcell/definition_spec.rb b/bosh-stemcell/spec/bosh/stemcell/definition_spec.rb index f66dbb1e10..81fd318a28 100644 --- a/bosh-stemcell/spec/bosh/stemcell/definition_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/definition_spec.rb @@ -41,7 +41,7 @@ module Bosh::Stemcell definition = instance_double("Bosh::Stemcell::Definition") expect(Bosh::Stemcell::Definition) .to receive(:new) - .with(infrastructure, hypervisor, operating_system) + .with(infrastructure, hypervisor, operating_system, "amd64") .and_return(definition) Bosh::Stemcell::Definition.for( @@ -51,12 +51,52 @@ module Bosh::Stemcell "operating-system-version" ) end + + it "passes an explicit architecture through to the new definition" do + expect(Bosh::Stemcell::Infrastructure) + .to receive(:for) + .with("infrastructure-name") + .and_return(infrastructure) + + expect(Bosh::Stemcell::OperatingSystem) + .to receive(:for) + .with("operating-system-name", "operating-system-version") + .and_return(operating_system) + + definition = instance_double("Bosh::Stemcell::Definition") + expect(Bosh::Stemcell::Definition) + .to receive(:new) + .with(infrastructure, hypervisor, operating_system, "arm64") + .and_return(definition) + + Bosh::Stemcell::Definition.for( + "infrastructure-name", + hypervisor, + "operating-system-name", + "operating-system-version", + "arm64" + ) + end end describe "#initialize" do its(:infrastructure) { should == infrastructure } its(:operating_system) { should == operating_system } its(:hypervisor_name) { should == hypervisor } + + it "defaults the architecture to amd64" do + expect(definition.architecture).to eq("amd64") + end + + context "when an architecture is provided" do + subject(:definition) do + Bosh::Stemcell::Definition.new(infrastructure, hypervisor, operating_system, "arm64") + end + + it "uses the provided architecture" do + expect(definition.architecture).to eq("arm64") + end + end end describe "#==" do @@ -113,6 +153,26 @@ module Bosh::Stemcell ) end end + + context "the architecture is the default (amd64)" do + it "leaves the architecture off the name" do + expect(definition.stemcell_name("disk-format")).to eq( + "infrastructure-name-hypervisor-operating-system-name-operating_system_version-disk-format" + ) + end + end + + context "the architecture is non-default (arm64)" do + subject(:definition) do + Bosh::Stemcell::Definition.new(infrastructure, hypervisor, operating_system, "arm64") + end + + it "appends the architecture to the name" do + expect(definition.stemcell_name("disk-format")).to eq( + "infrastructure-name-hypervisor-operating-system-name-operating_system_version-disk-format-arm64" + ) + end + end end describe "disk_formats" do diff --git a/bosh-stemcell/spec/os_image/ubuntu_spec.rb b/bosh-stemcell/spec/os_image/ubuntu_spec.rb index 95a8d57aa9..95f29d2a5f 100644 --- a/bosh-stemcell/spec/os_image/ubuntu_spec.rb +++ b/bosh-stemcell/spec/os_image/ubuntu_spec.rb @@ -32,9 +32,16 @@ describe "base_apt" do describe file("/etc/apt/sources.list") do - its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble main universe multiverse' } - its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-updates main universe multiverse' } - its(:content) { should match 'deb http:\/\/(security|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-security main universe multiverse' } + if Bosh::Stemcell::Arch.x86_64? + its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble main universe multiverse' } + its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-updates main universe multiverse' } + its(:content) { should match 'deb http:\/\/(security|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-security main universe multiverse' } + else + # ARM64 (and other non-amd64 arches) are served from the ports mirror. + its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble main universe multiverse' } + its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble-updates main universe multiverse' } + its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble-security main universe multiverse' } + end end describe file("/lib/systemd/system/monit.service") do @@ -85,14 +92,18 @@ end context "installed by system_grub" do - %w[ - grub2 - ].each do |pkg| + # ARM64 has no "grub2" metapackage; grub-efi-arm64 is the equivalent. + grub_packages = Bosh::Stemcell::Arch.x86_64? ? %w[grub2] : %w[grub-efi-arm64] + grub_packages.each do |pkg| describe package(pkg) do it { should be_installed } end end - %w[unicode.pf2 menu.lst gfxblacklist.txt].each do |grub_stage| + # gfxblacklist.txt ships with the amd64 grub2 metapackage and is not present + # in the arm64 grub-efi-arm64 package set. + grub_files = %w[unicode.pf2 menu.lst] + grub_files << "gfxblacklist.txt" if Bosh::Stemcell::Arch.x86_64? + grub_files.each do |grub_stage| describe file("/boot/grub/#{grub_stage}") do it { should be_file } end @@ -162,7 +173,8 @@ end context "PAM configuration" do - describe file("/lib/x86_64-linux-gnu/security/pam_pwquality.so") do + pam_multiarch_triplet = Bosh::Stemcell::Arch.x86_64? ? "x86_64-linux-gnu" : "aarch64-linux-gnu" + describe file("/lib/#{pam_multiarch_triplet}/security/pam_pwquality.so") do it { should be_file } end diff --git a/ci/tasks/build.sh b/ci/tasks/build.sh index c8ae18bcac..3a3cd1397e 100755 --- a/ci/tasks/build.sh +++ b/ci/tasks/build.sh @@ -77,6 +77,10 @@ chown -R ubuntu:ubuntu "${REPO_PARENT}/bosh-linux-stemcell-builder" chown -R ubuntu:ubuntu /mnt sudo chmod u+s "$(which sudo)" +# Architecture of the stemcell to build. Defaults to amd64 so existing pipelines +# are unaffected; set STEMCELL_ARCH=arm64 to build an ARM64 stemcell. +STEMCELL_ARCH="${STEMCELL_ARCH:-amd64}" + # pass SHLVL or '~ubuntu/.bash_logout' will exit 1 sudo --set-home --user ubuntu \ --preserve-env=GEM_HOME,SHLVL,UBUNTU_ADVANTAGE_TOKEN,UBUNTU_FIPS_USE_IAAS_KERNEL \ @@ -86,7 +90,7 @@ set -e cd "${REPO_PARENT}/bosh-linux-stemcell-builder" bundle install -bundle exec rake "stemcell:build[${IAAS},${HYPERVISOR},${OS_NAME},${OS_VERSION},${OS_IMAGE},${CANDIDATE_BUILD_NUMBER}]" +bundle exec rake "stemcell:build[${IAAS},${HYPERVISOR},${OS_NAME},${OS_VERSION},${OS_IMAGE},${CANDIDATE_BUILD_NUMBER},${STEMCELL_ARCH}]" SUDO # diff --git a/ci/tasks/build.yml b/ci/tasks/build.yml index beae4ce7f0..aef737e2e7 100644 --- a/ci/tasks/build.yml +++ b/ci/tasks/build.yml @@ -23,6 +23,7 @@ params: HYPERVISOR: replace-me OS_NAME: replace-me OS_VERSION: replace-me + STEMCELL_ARCH: amd64 STEMCELL_BUCKET: replace-me S3_API_ENDPOINT: s3.amazonaws.com ESM_TOKEN: diff --git a/ci/tasks/os-images/build.sh b/ci/tasks/os-images/build.sh index 5b55f27f79..344e6c0635 100755 --- a/ci/tasks/os-images/build.sh +++ b/ci/tasks/os-images/build.sh @@ -35,6 +35,10 @@ chown -R ubuntu:ubuntu "${REPO_PARENT}/bosh-linux-stemcell-builder" chown -R ubuntu:ubuntu /mnt sudo chmod u+s "$(which sudo)" +# Architecture of the OS image to build. Defaults to amd64 so existing pipelines +# are unaffected; set STEMCELL_ARCH=arm64 to build an ARM64 OS image. +STEMCELL_ARCH="${STEMCELL_ARCH:-amd64}" + # pass SHLVL or '~ubuntu/.bash_logout' will exit 1 sudo --set-home --user ubuntu \ --preserve-env=GEM_HOME,SHLVL,BUILD_TIME,UBUNTU_ADVANTAGE_TOKEN,UBUNTU_DEBOOTSTRAP_MIRROR \ @@ -44,5 +48,5 @@ set -e cd "${REPO_PARENT}/bosh-linux-stemcell-builder" bundle install -bundle exec rake "stemcell:build_os_image[${OPERATING_SYSTEM_NAME},${OPERATING_SYSTEM_VERSION},${OS_IMAGE}]" +bundle exec rake "stemcell:build_os_image[${OPERATING_SYSTEM_NAME},${OPERATING_SYSTEM_VERSION},${OS_IMAGE},${STEMCELL_ARCH}]" SUDO diff --git a/ci/tasks/os-images/build.yml b/ci/tasks/os-images/build.yml index 38de5860de..4405744385 100644 --- a/ci/tasks/os-images/build.yml +++ b/ci/tasks/os-images/build.yml @@ -17,6 +17,7 @@ run: params: OPERATING_SYSTEM_NAME: replace-me OPERATING_SYSTEM_VERSION: replace-me + STEMCELL_ARCH: amd64 ESM_TOKEN: UBUNTU_ADVANTAGE_TOKEN: UBUNTU_DEBOOTSTRAP_MIRROR: diff --git a/stemcell_builder/lib/helpers.sh b/stemcell_builder/lib/helpers.sh index 2ca4bbdaaa..ad5174cde6 100644 --- a/stemcell_builder/lib/helpers.sh +++ b/stemcell_builder/lib/helpers.sh @@ -94,3 +94,24 @@ function is_x86_64() { fi } +# Prints the Debian architecture (e.g. amd64, arm64) of the stemcell being built. +# Prefers the architecture of the bootstrapped root filesystem (set by +# debootstrap), which is reliably available to every stage regardless of how +# build settings are propagated. Falls back to the stemcell_arch build setting, +# then to amd64 so existing builds are unaffected. +# +# Usage: stemcell_target_arch [rootfs_path] +# rootfs_path defaults to $chroot; image stages that mount the rootfs +# elsewhere (e.g. $image_mount_point) should pass their path explicitly. +function stemcell_target_arch() { + local rootfs="${1:-${chroot:-}}" + local arch="" + if [ -n "$rootfs" ] && [ -x "$rootfs/usr/bin/dpkg" ]; then + arch="$(run_in_chroot "$rootfs" "dpkg --print-architecture" 2>/dev/null | tr -d '[:space:]')" + fi + if [ -z "$arch" ]; then + arch="${stemcell_arch:-amd64}" + fi + echo "$arch" +} + diff --git a/stemcell_builder/stages/base_apt/apply.sh b/stemcell_builder/stages/base_apt/apply.sh index 276a84b67e..f7dcf6c109 100755 --- a/stemcell_builder/stages/base_apt/apply.sh +++ b/stemcell_builder/stages/base_apt/apply.sh @@ -8,6 +8,19 @@ source $base_dir/lib/prelude_apply.bash mount --bind /sys "$chroot/sys" add_on_exit "umount $chroot/sys" +# Ubuntu serves non-amd64 architectures (e.g. arm64) from the ports mirror +# (ports.ubuntu.com/ubuntu-ports) rather than archive/security.ubuntu.com. +# stemcell_target_arch derives the arch from the bootstrapped chroot, so this +# works regardless of how build settings are propagated. Defaults to the amd64 +# mirrors for amd64/unknown so existing builds are unchanged. +if [ "$(stemcell_target_arch)" == "arm64" ]; then + apt_archive_mirror="http://ports.ubuntu.com/ubuntu-ports" + apt_security_mirror="http://ports.ubuntu.com/ubuntu-ports" +else + apt_archive_mirror="http://archive.ubuntu.com/ubuntu" + apt_security_mirror="http://security.ubuntu.com/ubuntu" +fi + # check if current git branch is a tag and use snapshot date from the last commit message in that tag if [ -n "${BUILD_TIME:-}" ]; then cat > "$chroot/etc/apt/sources.list" < "$chroot/etc/apt/sources.list" < ${image_mount_point}/boot/grub/device.map echo "(hd0) ${device}" > ${image_mount_point}/device.map # fallback for non-UEFI systems # install bootsector into disk image file -run_in_chroot ${image_mount_point} "grub-install --target=x86_64-efi --efi-directory=/boot/efi --boot-directory=/boot/efi/EFI --removable -v --no-floppy ${device}" -run_in_chroot ${image_mount_point} "grub-install -v --target=i386-pc --grub-mkdevicemap=/device.map --no-floppy ${device}" # fallback for non-UEFI systems +if [ "$(stemcell_target_arch ${image_mount_point})" == "arm64" ]; then + # ARM64 is UEFI-only; there is no i386-pc/BIOS fallback target. + run_in_chroot ${image_mount_point} "grub-install --target=arm64-efi --efi-directory=/boot/efi --boot-directory=/boot/efi/EFI --removable -v --no-floppy ${device}" +else + run_in_chroot ${image_mount_point} "grub-install --target=x86_64-efi --efi-directory=/boot/efi --boot-directory=/boot/efi/EFI --removable -v --no-floppy ${device}" + run_in_chroot ${image_mount_point} "grub-install -v --target=i386-pc --grub-mkdevicemap=/device.map --no-floppy ${device}" # fallback for non-UEFI systems +fi mkdir -p ${image_mount_point}/boot/efi/boot/grub cp ${image_mount_point}/boot/efi/EFI/BOOT/grub.cfg ${image_mount_point}/boot/efi/boot/grub/grub.cfg diff --git a/stemcell_builder/stages/password_policies/apply.sh b/stemcell_builder/stages/password_policies/apply.sh index 86e78dde3d..d63d717e46 100755 --- a/stemcell_builder/stages/password_policies/apply.sh +++ b/stemcell_builder/stages/password_policies/apply.sh @@ -34,12 +34,18 @@ strip_trailing_whitespace_from $chroot/etc/pam.d/common-password patch -p1 $chroot/etc/pam.d/common-password < $assets_dir/ubuntu/common-password.patch # libpam-lastlog2 installs pam_lastlog2.so only to the multiarch path -# (/usr/lib/x86_64-linux-gnu/security/) but PAM's securedir is /usr/lib/security/. -# Bridge the gap so PAM can load the module referenced above. -if [ -f "$chroot/usr/lib/x86_64-linux-gnu/security/pam_lastlog2.so" ] && \ +# (/usr/lib//security/) but PAM's securedir is /usr/lib/security/. +# Bridge the gap so PAM can load the module referenced above. The multiarch +# triplet depends on the target architecture. +if [ "$(stemcell_target_arch)" == "arm64" ]; then + multiarch_triplet="aarch64-linux-gnu" +else + multiarch_triplet="x86_64-linux-gnu" +fi +if [ -f "$chroot/usr/lib/${multiarch_triplet}/security/pam_lastlog2.so" ] && \ [ ! -e "$chroot/usr/lib/security/pam_lastlog2.so" ]; then mkdir -p "$chroot/usr/lib/security" - ln -sf /usr/lib/x86_64-linux-gnu/security/pam_lastlog2.so \ + ln -sf "/usr/lib/${multiarch_triplet}/security/pam_lastlog2.so" \ "$chroot/usr/lib/security/pam_lastlog2.so" fi diff --git a/stemcell_builder/stages/system_grub/apply.sh b/stemcell_builder/stages/system_grub/apply.sh index 60798c3b0c..9227003ad4 100755 --- a/stemcell_builder/stages/system_grub/apply.sh +++ b/stemcell_builder/stages/system_grub/apply.sh @@ -5,7 +5,14 @@ set -e base_dir=$(readlink -nf $(dirname $0)/../..) source $base_dir/lib/prelude_apply.bash -pkg_mgr install grub2 grub-efi-amd64-bin grub-efi-amd64-signed shim-signed +# Install the GRUB EFI packages matching the target architecture. Defaults to +# amd64 so existing builds are unchanged; arm64 uses the arm64 EFI packages. +if [ "$(stemcell_target_arch)" == "arm64" ]; then + # ARM64 has no "grub2" metapackage; grub-efi-arm64 is the equivalent. + pkg_mgr install grub-efi-arm64 grub-efi-arm64-bin grub-efi-arm64-signed shim-signed +else + pkg_mgr install grub2 grub-efi-amd64-bin grub-efi-amd64-signed shim-signed +fi # When a kernel is installed, update-grub is run per /etc/kernel-img.conf. # It complains when /boot/grub/menu.lst doesn't exist, so create it. From fa18269a69d55dbfc6e39c3c72c5adf5611bbcf1 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Thu, 24 Sep 2026 08:09:39 -0700 Subject: [PATCH 02/16] ci: Add arm64 os-image build job and arch-aware builder image Add an arm64 (Graviton) OS image build to the stemcell pipeline and make the os-image-stemcell-builder image build for arm64, per RFC cloudfoundry/community#1530. Builder image (os-image-stemcell-builder/Dockerfile): - Split into stages so the VMware ovftool install runs on amd64 only. VMware ships no arm64 Linux ovftool, and vSphere is not an arm64 target, so the arm64 stage skips ovftool entirely and stays buildable without an ovftool bundle in the build context. Pipeline (pipeline-template.yml): - Add build-os-image-arm64, mirroring build-os-image but pinned to an arm64 worker and using the ports.ubuntu.com mirror that hosts arm64 Ubuntu packages. It publishes to its own os-image-tarball-arm64 resource and version series so it never perturbs the amd64 artifacts. The job stays dormant until an arm64-tagged worker is registered (RFC D3 / Open Question 2). Downstream heavy/light IaaS packaging and arm64 AMI/GCE publishing are platform-owned (RFC D5) and tracked separately. The amd64 build path is unchanged. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../os-image-stemcell-builder/Dockerfile | 33 ++++++- ci/pipeline-template.yml | 92 +++++++++++++++++++ 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/ci/docker/os-image-stemcell-builder/Dockerfile b/ci/docker/os-image-stemcell-builder/Dockerfile index 40fc8e6f1a..1ca0472515 100644 --- a/ci/docker/os-image-stemcell-builder/Dockerfile +++ b/ci/docker/os-image-stemcell-builder/Dockerfile @@ -1,6 +1,6 @@ # bosh/os-image-stemcell-builder ARG BASE_IMAGE -FROM $BASE_IMAGE +FROM $BASE_IMAGE AS base LABEL maintainer="cf-bosh@lists.cloudfoundry.org" @@ -13,9 +13,6 @@ ARG RUBY_INSTALL_URL ARG RUBY_VERSION ARG GEM_HOME -ARG OVF_TOOL_INSTALLER -ARG OVF_TOOL_INSTALLER_SHA1 - # ENV VARs ENV DEBIAN_FRONTEND="noninteractive" ENV LANG="en_US.UTF-8" @@ -78,7 +75,19 @@ RUN temp_dir="/mnt/tmp" \ RUN mkdir -p /mnt/stemcells \ && chown -R ubuntu:ubuntu /mnt/stemcells -# VMware's ovftool is used to create vSphere stemcells +# VMware's ovftool is used to create vSphere stemcells. VMware ships no arm64 +# Linux ovftool, and vSphere is not an arm64 (Graviton) target, so the ovftool +# bundle is only added and installed on amd64. On arm64 the ADD is skipped +# entirely (a conditional build stage), which keeps the arm64 image buildable +# without an ovftool bundle in the build context. +# RFC: cloudfoundry/community#1530. +# +# TARGETARCH is provided automatically by BuildKit ("amd64" / "arm64"). +ARG TARGETARCH + +FROM base AS ovftool-amd64 +ARG OVF_TOOL_INSTALLER +ARG OVF_TOOL_INSTALLER_SHA1 ADD ${OVF_TOOL_INSTALLER} /tmp/ovftool_installer.bundle RUN cd /tmp \ && echo "${OVF_TOOL_INSTALLER_SHA1} /tmp/ovftool_installer.bundle" | sha1sum -c - \ @@ -87,6 +96,20 @@ RUN cd /tmp \ && bash ./ovftool_installer.bundle --eulas-agreed \ && rm -rf ./ovftool_installer.bundle /tmp/vmware-root/ /usr/local/bin/more +# On arm64 the ovftool stage is a no-op passthrough of the base image. +FROM base AS ovftool-arm64 + +# Select the arch-appropriate ovftool stage and continue the build from it. +FROM ovftool-${TARGETARCH} AS builder + +# Build args are per-stage in BuildKit, so re-declare the ones used below. +ARG META4_CLI_URL +ARG SYFT_CLI_URL +ARG YQ_CLI_URL +ARG RUBY_INSTALL_URL +ARG RUBY_VERSION +ARG GEM_HOME + RUN meta4_cli_path="/usr/local/bin/meta4" \ && curl --show-error -sL "${META4_CLI_URL}" \ > "${meta4_cli_path}" \ diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index 807fce0a37..85d72db794 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -447,6 +447,66 @@ jobs: params: file: os-image-version/number +#! arm64 (Graviton) OS image build. RFC: cloudfoundry/community#1530. +#! Mirrors build-os-image but runs the same bosh-stemcells-ci os-images/build.yml +#! task on an arm64 worker. The stemcell code derives target arch from the chroot +#! (dpkg --print-architecture), and this job supplies the ports.ubuntu.com mirror +#! that hosts arm64 Ubuntu packages. It publishes to its own os-image-tarball-arm64 +#! resource and version series so it never perturbs the amd64 os-image artifacts. +#! Dormant until the platform team registers an `arm64`-tagged Concourse worker +#! (RFC D3 / Open Question 2). Heavy/light stemcell packaging + arm64 AMI/GCE +#! publishing for downstream IaaSes is platform-owned (RFC D5) and tracked +#! separately; this job covers the os-image stage whose arch-correctness lives +#! in this repo (validated 376/0 on a Graviton instance). +- name: build-os-image-arm64 + serial: true + plan: + - put: build-time + tags: [ arm64 ] + - get: bosh-stemcells-ci + tags: [ arm64 ] + - get: os-image-stemcell-builder-registry-image + tags: [ arm64 ] + - get: bosh-linux-stemcell-builder + trigger: true + tags: [ arm64 ] + - get: os-image-version-arm64 + params: + bump: "major" + tags: [ arm64 ] + - get: usn-log + tags: [ arm64 ] + - get: stemcell-trigger + trigger: true + tags: [ arm64 ] + - task: build + file: bosh-stemcells-ci/ci/tasks/os-images/build.yml + image: os-image-stemcell-builder-registry-image + tags: [ arm64 ] + params: + OPERATING_SYSTEM_NAME: ubuntu + OPERATING_SYSTEM_VERSION: (@= data.values.stemcell_details.os_short_name @) + UBUNTU_DEBOOTSTRAP_MIRROR: http://ports.ubuntu.com/ubuntu-ports + privileged: true + vars: + image_os_tag: (@= data.values.stemcell_details.os_short_name @) + - put: os-image-tarball-arm64 + tags: [ arm64 ] + params: + files: + - os-image/(@= data.values.stemcell_details.os_name @).tgz + - usn-log/usn-log.json + rename: (@= data.values.stemcell_details.branch @)/(@= data.values.stemcell_details.os_name @)-arm64.meta4 + options: + author_email: (@= data.values.stemcell_details.bot_email @) + author_name: (@= data.values.stemcell_details.bot_name @) + message: 'Bump os-image tgz (arm64)' + version: os-image-version-arm64/version + - put: os-image-version-arm64 + tags: [ arm64 ] + params: + file: os-image-version-arm64/number + - name: test-unit plan: - get: bosh-stemcells-ci @@ -1657,6 +1717,27 @@ resources: secret_key: ((hmac_secret)) type: s3 +#! arm64 (Graviton) OS image tarball. RFC: cloudfoundry/community#1530. +#! Mirrors os-image-tarball but under a distinct -arm64 meta4 name and S3 prefix +#! so the arm64 os-image never overwrites the amd64 one. +- name: os-image-tarball-arm64 + type: metalink-repository + source: + mirror_files: + - destination: s3://storage.googleapis.com/bosh-os-images/(@= data.values.stemcell_details.branch @)-arm64/{{.Name}} + options: + private_key: ((github_deploy_key_bosh-linux-stemcell-builder.private_key)) + filters: + - repositorypath: "(@= data.values.stemcell_details.branch @)/(@= data.values.stemcell_details.os_name @)-arm64.meta4" + uri: git+ssh://git@github.com/cloudfoundry/bosh-linux-stemcell-builder.git//image-metalinks/#(@= data.values.stemcell_details.branch @) + url_handlers: + - include: + - (s3|https)://.* + options: + access_key: ((hmac_accesskey)) + secret_key: ((hmac_secret)) + type: s3 + - name: version type: semver source: @@ -1675,6 +1756,17 @@ resources: file: (@= data.values.stemcell_details.branch @)/os-image-version private_key: ((github_deploy_key_bosh-linux-stemcell-builder.private_key)) +#! arm64 (Graviton) os-image version series, independent of the amd64 series. +#! RFC: cloudfoundry/community#1530. +- name: os-image-version-arm64 + type: semver + source: + driver: git + uri: git@github.com:cloudfoundry/bosh-linux-stemcell-builder + branch: release-details + file: (@= data.values.stemcell_details.branch @)/os-image-version-arm64 + private_key: ((github_deploy_key_bosh-linux-stemcell-builder.private_key)) + - name: bosh-linux-stemcell-builder-push type: git source: From 4c543889895dafc378e60acf84c4e037669f2586 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Thu, 24 Sep 2026 15:09:47 -0700 Subject: [PATCH 03/16] fix(ci): Build arm64 os-image in an arm64 builder image The build-os-image-arm64 job added earlier would not have produced an arm64 OS image: - It did not set STEMCELL_ARCH, so ci/tasks/os-images/build.yml defaulted to amd64 and debootstrap ran --arch=amd64 against ports.ubuntu.com, which does not serve amd64. - It ran in the amd64-only os-image-stemcell-builder image on an arm64 worker. Changes: - build-os-image-arm64: set STEMCELL_ARCH=arm64 and run in the new arm64 builder image. - Add build-os-image-stemcell-builder-arm64, mirroring the amd64 builder job on an arm64 worker and pushing to a separate "-arm64" tag, plus the os-image-stemcell-builder-registry-image-arm64 resource. - ci/tasks/build-docker-args.sh: select meta4, syft and yq assets for the worker's architecture. meta4 publishes no arm64 binary, so on arm64 META4_CLI_URL is left empty and META4_VERSION is emitted instead. - os-image-stemcell-builder Dockerfile: download the prebuilt meta4 when META4_CLI_URL is set (amd64, unchanged) and otherwise build it from source with the image's Go toolchain. amd64 behaviour is unchanged: the amd64 selectors resolve to the same assets as before and the prebuilt meta4 download path is kept. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../os-image-stemcell-builder/Dockerfile | 15 +++- ci/pipeline-template.yml | 77 +++++++++++++++++-- ci/tasks/build-docker-args.sh | 21 ++++- 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/ci/docker/os-image-stemcell-builder/Dockerfile b/ci/docker/os-image-stemcell-builder/Dockerfile index 1ca0472515..42e228223c 100644 --- a/ci/docker/os-image-stemcell-builder/Dockerfile +++ b/ci/docker/os-image-stemcell-builder/Dockerfile @@ -104,15 +104,26 @@ FROM ovftool-${TARGETARCH} AS builder # Build args are per-stage in BuildKit, so re-declare the ones used below. ARG META4_CLI_URL +ARG META4_VERSION ARG SYFT_CLI_URL ARG YQ_CLI_URL ARG RUBY_INSTALL_URL ARG RUBY_VERSION ARG GEM_HOME +# Use the prebuilt meta4 release binary when one exists for this architecture +# (amd64). dpb587/metalink publishes no arm64 binary, so on arm64 +# build-docker-args.sh leaves META4_CLI_URL empty and meta4 is built from source +# with the image's Go toolchain instead. RUN meta4_cli_path="/usr/local/bin/meta4" \ - && curl --show-error -sL "${META4_CLI_URL}" \ - > "${meta4_cli_path}" \ + && if [ -n "${META4_CLI_URL}" ]; then \ + curl --show-error -sL "${META4_CLI_URL}" > "${meta4_cli_path}"; \ + else \ + git clone --depth 1 --branch "${META4_VERSION}" \ + https://github.com/dpb587/metalink.git /tmp/metalink \ + && ( cd /tmp/metalink && go build -o "${meta4_cli_path}" ./cli ) \ + && rm -rf /tmp/metalink; \ + fi \ && chmod +x "${meta4_cli_path}" ENV GEM_HOME="${GEM_HOME}" \ diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index 85d72db794..2dc77b304f 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -187,6 +187,7 @@ groups: - name: infrastructure-tending jobs: - build-os-image-stemcell-builder + - build-os-image-stemcell-builder-arm64 - ensure-integration-network - cleanup-published-* - cleanup-unpublished-* @@ -249,6 +250,57 @@ jobs: params: image: image/image.tar +#! arm64 (Graviton) builder image. RFC: cloudfoundry/community#1530. +#! Mirrors build-os-image-stemcell-builder on an arm64 worker so build-os-image-arm64 +#! runs in a native arm64 image. The Dockerfile skips VMware ovftool on arm64 (no +#! arm64 ovftool exists; vSphere is not an arm64 target), and build-docker-args.sh +#! selects arm64 CLI assets from the worker's architecture. Pushes to a separate +#! "-arm64" tag so the amd64 image is never overwritten. Dormant until an +#! `arm64`-tagged worker is registered (RFC D3 / Open Question 2). +- name: build-os-image-stemcell-builder-arm64 + public: true + serial: true + plan: + - get: daily + trigger: true + tags: [ arm64 ] + - get: ubuntu_base + tags: [ arm64 ] + - get: os-image-stemcell-builder-dockerfile + trigger: true + tags: [ arm64 ] + - get: bosh-stemcells-ci + tags: [ arm64 ] + - task: build-docker-args + file: bosh-stemcells-ci/ci/tasks/build-docker-args.yml + image: ubuntu_base + tags: [ arm64 ] + - task: build-image + privileged: true + tags: [ arm64 ] + config: + platform: linux + image_resource: + type: registry-image + source: + repository: concourse/oci-build-task + inputs: + - name: os-image-stemcell-builder-dockerfile + - name: docker-build-args + outputs: + - name: image + params: + CONTEXT: os-image-stemcell-builder-dockerfile/ci/docker/os-image-stemcell-builder + BUILD_ARGS_FILE: docker-build-args/docker-build-args.yml + BUILD_ARG_BASE_IMAGE: ubuntu:(@= data.values.stemcell_details.os_short_name @) + run: + path: build + - put: os-image-stemcell-builder-registry-image-arm64 + no_get: true + tags: [ arm64 ] + params: + image: image/image.tar + #! Manually triggered job that idempotently ensures the GCP subnetwork and #! firewall rule consumed by deploy-director / cleanup-bats-vms / prepare-bats #! in the test-stemcells-ipv4 and bats jobs below exist. GCP is the source of @@ -448,10 +500,12 @@ jobs: file: os-image-version/number #! arm64 (Graviton) OS image build. RFC: cloudfoundry/community#1530. -#! Mirrors build-os-image but runs the same bosh-stemcells-ci os-images/build.yml -#! task on an arm64 worker. The stemcell code derives target arch from the chroot -#! (dpkg --print-architecture), and this job supplies the ports.ubuntu.com mirror -#! that hosts arm64 Ubuntu packages. It publishes to its own os-image-tarball-arm64 +#! Mirrors build-os-image but runs the same ci/tasks/os-images/build.yml task on +#! an arm64 worker, inside the arm64 builder image. STEMCELL_ARCH=arm64 selects +#! the target architecture (passed to the rake task -> debootstrap --arch), and +#! this job supplies the ports.ubuntu.com mirror that hosts arm64 Ubuntu +#! packages. Later stages read the arch back from the rootfs +#! (stemcell_target_arch). It publishes to its own os-image-tarball-arm64 #! resource and version series so it never perturbs the amd64 os-image artifacts. #! Dormant until the platform team registers an `arm64`-tagged Concourse worker #! (RFC D3 / Open Question 2). Heavy/light stemcell packaging + arm64 AMI/GCE @@ -465,7 +519,7 @@ jobs: tags: [ arm64 ] - get: bosh-stemcells-ci tags: [ arm64 ] - - get: os-image-stemcell-builder-registry-image + - get: os-image-stemcell-builder-registry-image-arm64 tags: [ arm64 ] - get: bosh-linux-stemcell-builder trigger: true @@ -481,11 +535,12 @@ jobs: tags: [ arm64 ] - task: build file: bosh-stemcells-ci/ci/tasks/os-images/build.yml - image: os-image-stemcell-builder-registry-image + image: os-image-stemcell-builder-registry-image-arm64 tags: [ arm64 ] params: OPERATING_SYSTEM_NAME: ubuntu OPERATING_SYSTEM_VERSION: (@= data.values.stemcell_details.os_short_name @) + STEMCELL_ARCH: arm64 UBUNTU_DEBOOTSTRAP_MIRROR: http://ports.ubuntu.com/ubuntu-ports privileged: true vars: @@ -1958,6 +2013,16 @@ resources: username: ((dockerhub_username)) password: ((dockerhub_password)) +#! arm64 (Graviton) builder image, published under its own tag so the amd64 +#! image is never overwritten. RFC: cloudfoundry/community#1530. +- name: os-image-stemcell-builder-registry-image-arm64 + type: registry-image + source: + repository: bosh/os-image-stemcell-builder + tag: (@= data.values.stemcell_details.os_short_name @)-arm64 + username: ((dockerhub_username)) + password: ((dockerhub_password)) + - name: bosh-os-image-builder-vmware-ovftool type: gcs-resource source: diff --git a/ci/tasks/build-docker-args.sh b/ci/tasks/build-docker-args.sh index d56f2c02c0..cdf9012334 100755 --- a/ci/tasks/build-docker-args.sh +++ b/ci/tasks/build-docker-args.sh @@ -18,12 +18,24 @@ export TZ="Etc/UTC" apt-get update -y apt-get install -y --no-install-recommends ca-certificates curl jq -meta4_cli_url="$(curl -s https://api.github.com/repos/dpb587/metalink/releases/latest \ - | jq -r '.assets[] | select(.name | match("meta4-[0-9]+.[0-9]+.[0-9]+-linux-amd64")) | .browser_download_url')" +# Select CLI assets for the architecture this task runs on, so the builder image +# can be built for both amd64 and arm64 (Graviton) workers. +# RFC: https://github.com/cloudfoundry/community/pull/1530 +case "$(uname -m)" in + aarch64|arm64) arch="arm64" ;; + *) arch="amd64" ;; +esac + +meta4_release="$(curl -s https://api.github.com/repos/dpb587/metalink/releases/latest)" +meta4_cli_url="$(jq -r --arg a "linux-${arch}" '.assets[] | select(.name | test("^meta4-[0-9]+\\.[0-9]+\\.[0-9]+-" + $a + "$")) | .browser_download_url' <<< "${meta4_release}")" +# dpb587/metalink publishes linux-amd64 only. When no prebuilt binary exists for +# this arch, META4_CLI_URL is left empty and the Dockerfile builds meta4 from +# source at META4_VERSION instead. +meta4_version="$(jq -r '.tag_name' <<< "${meta4_release}")" syft_cli_url="$(curl -s https://api.github.com/repos/anchore/syft/releases/latest \ - | jq -r '.assets[] | select(.name | endswith ("_linux_amd64.tar.gz")) | .browser_download_url')" + | jq -r --arg a "_linux_${arch}.tar.gz" '.assets[] | select(.name | endswith ($a)) | .browser_download_url')" yq_cli_url="$(curl -s https://api.github.com/repos/mikefarah/yq/releases/latest \ - | jq -r '.assets[] | select(.name | endswith ("linux_amd64")) | .browser_download_url')" + | jq -r --arg a "linux_${arch}" '.assets[] | select(.name | endswith ($a)) | .browser_download_url')" ruby_install_url="$(curl -s https://api.github.com/repos/postmodern/ruby-install/releases/latest \ | jq -r '.assets[] | select(.name | endswith ("tar.gz")) | .browser_download_url')" @@ -33,6 +45,7 @@ gem_home="/usr/local/bundle" cat << EOF > "${REPO_PARENT}/docker-build-args/docker-build-args.yml" META4_CLI_URL: "${meta4_cli_url}" +META4_VERSION: "${meta4_version}" SYFT_CLI_URL: "${syft_cli_url}" YQ_CLI_URL: "${yq_cli_url}" RUBY_INSTALL_URL: "${ruby_install_url}" From 38bf38017ee8e20beb84f845e44ba62a61780b64 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Thu, 1 Oct 2026 08:58:33 -0700 Subject: [PATCH 04/16] feat: Make the stemcell build stage arm64-capable The os-image stage already supported arm64, but `rake stemcell:build` assembled an amd64-only stemcell: it installed the linux-amd64 agent, the amd64 blobstore CLIs, and the x86_64 static-library list, and named the artifact without an arch suffix. This completes the stage so an arm64 (Graviton) AWS stemcell can be built end to end. - bosh_go_agent: install bosh-agent--linux- via the agent metalink, and use the meta4 provided by the builder image on non-x86_64 hosts (dpb587/metalink publishes linux-amd64 only). - blobstore_clis: on non-amd64, read .linux-.url/.sha256sum and fail with a clear message when a build is not configured. - static_libraries_config: rewrite the x86_64-linux-gnu multiarch triplet to aarch64-linux-gnu on arm64. - ci/tasks/build.sh: append an "-" suffix to the stemcell name and index path for non-amd64 builds, matching Definition#stemcell_name. - ci/pipeline-template.yml: add a dormant build-aws-xen-hvm-arm64 job and aws-xen-hvm-arm64 index resource that build on an arm64 worker and publish to a separate index, so amd64 artifacts are untouched. - stemcell_shared_examples: skip the /lib64 ownership check when the path is absent (arm64 uses /lib/ld-linux-aarch64.so.1), mirroring /usr/lib64. Validated on a Graviton (t4g) instance: the arm64 AWS stemcell builds and the resulting image carries an aarch64 agent, aarch64 blobstore CLIs, arm64-efi GRUB (BOOTAA64.EFI), and architecture: aarch64 in stemcell.MF. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../spec/support/stemcell_shared_examples.rb | 7 +- ci/pipeline-template.yml | 92 +++++++++++++++++++ ci/tasks/build.sh | 15 ++- .../stages/blobstore_clis/config.sh | 19 +++- .../stages/bosh_go_agent/apply.sh | 25 ++++- .../stages/static_libraries_config/apply.sh | 6 ++ 6 files changed, 152 insertions(+), 12 deletions(-) diff --git a/bosh-stemcell/spec/support/stemcell_shared_examples.rb b/bosh-stemcell/spec/support/stemcell_shared_examples.rb index d8e7bde462..6f57263bbd 100644 --- a/bosh-stemcell/spec/support/stemcell_shared_examples.rb +++ b/bosh-stemcell/spec/support/stemcell_shared_examples.rb @@ -34,8 +34,11 @@ it("should be owned by root user (stig: V-38466)") { should be_owned_by("root") } end - describe file("/lib64") do - it("should be owned by root user (stig: V-38466)") { should be_owned_by("root") } + # /lib64 holds the x86_64 dynamic linker and is absent on arm64, which uses + # /lib/ld-linux-aarch64.so.1. Skip the ownership check when it is missing, + # mirroring the /usr/lib64 check below. RFC: cloudfoundry/community#1530 + describe command('if [ -e /lib64 ]; then stat -c "%U" /lib64 ; else echo "root" ; fi') do + its(:stdout) { should eq("root\n") } end describe file("/usr/lib") do diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index 2dc77b304f..5c03132e45 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -751,6 +751,78 @@ jobs: - #@ build_stemcell(iaas.iaas, iaas.hypervisor, "-fips") #@ end +#! arm64 (Graviton) AWS stemcell. RFC: cloudfoundry/community#1530. +#! Mirrors build-aws-xen-hvm on an arm64 worker: it builds the stemcell from the +#! arm64 os-image (build-os-image-arm64) inside the arm64 builder image with +#! STEMCELL_ARCH=arm64. The stemcell is named ...-ubuntu--arm64 and indexed +#! under aws-xen-hvm-arm64.meta4, so it never collides with the amd64 stemcell. +#! It is not an input to test-stemcells-*, bats or aggregate-candidate-stemcells. +#! Dormant until an `arm64`-tagged worker is registered (RFC D3 / Open Question +#! 2). It also needs linux-arm64 builds of the bosh-agent (listed in the agent +#! metalink) and of the four blobstore CLIs (bosh-blobstore-*.linux-arm64.url / +#! .sha256sum assets in stemcell_builder/stages/blobstore_clis). +- name: build-aws-xen-hvm-arm64 + serial: true + plan: + - in_parallel: + - get: version + passed: + - build-stemcell + resource: version + trigger: true + tags: [ arm64 ] + - get: bosh-stemcells-ci + tags: [ arm64 ] + - get: os-image-stemcell-builder-registry-image-arm64 + tags: [ arm64 ] + - get: build-time + passed: + - build-stemcell + trigger: true + tags: [ arm64 ] + - get: bosh-linux-stemcell-builder + passed: + - build-stemcell + resource: bosh-linux-stemcell-builder + tags: [ arm64 ] + - get: stemcells-index + tags: [ arm64 ] + - get: os-image-tarball + resource: os-image-tarball-arm64 + passed: + - build-os-image-arm64 + tags: [ arm64 ] + - task: create-stemcell + file: bosh-stemcells-ci/ci/tasks/build.yml + image: os-image-stemcell-builder-registry-image-arm64 + tags: [ arm64 ] + params: + HYPERVISOR: xen-hvm + IAAS: aws + OS_NAME: ubuntu + S3_API_ENDPOINT: storage.googleapis.com + OS_VERSION: (@= data.values.stemcell_details.os_short_name @) + STEMCELL_ARCH: arm64 + STEMCELL_BUCKET: bosh-core-stemcells-candidate + GIT_USER_EMAIL: (@= data.values.stemcell_details.bot_email @) + GIT_USER_NAME: (@= data.values.stemcell_details.bot_name @) + AGENT_SUFFIX: (@= data.values.stemcell_details.agent_suffix @) + privileged: true + vars: + image_os_tag: (@= data.values.stemcell_details.os_short_name @) + - put: aws-xen-hvm-arm64 + attempts: 3 + tags: [ arm64 ] + params: + files: + - stemcell/*.tgz + rename: "{{.Version}}/aws-xen-hvm(@= data.values.stemcell_details.agent_suffix @)-arm64.meta4" + options: + author_email: (@= data.values.stemcell_details.bot_email @) + author_name: (@= data.values.stemcell_details.bot_name @) + message: 'dev: (@= data.values.stemcell_details.os_name @) (arm64)' + version: candidate-build-number/number + - name: bats serial: true plan: @@ -1754,6 +1826,26 @@ resources: - #@ metalink_resource(iaas.iaas, iaas.hypervisor, "-fips") #@ end +#! arm64 (Graviton) AWS stemcell dev index entry (aws-xen-hvm-arm64.meta4), +#! published by build-aws-xen-hvm-arm64. RFC: cloudfoundry/community#1530. +- name: aws-xen-hvm-arm64 + type: metalink-repository + source: + mirror_files: + - destination: s3://storage.googleapis.com/bosh-core-stemcells-candidate/aws/{{.Name}} + options: + private_key: ((github_deploy_key_bosh-io-stemcells-core-index.private_key)) + filters: + - repositorypath: "*/aws-xen-hvm(@= data.values.stemcell_details.agent_suffix @)-arm64.meta4" + uri: git+ssh://git@github.com:cloudfoundry/bosh-io-stemcells-core-index.git//dev/(@= data.values.stemcell_details.os_name @)/ + url_handlers: + - include: + - (s3|https)://.* + options: + access_key: ((hmac_accesskey)) + secret_key: ((hmac_secret)) + type: s3 + - name: os-image-tarball type: metalink-repository source: diff --git a/ci/tasks/build.sh b/ci/tasks/build.sh index 3a3cd1397e..eff6d7a64e 100755 --- a/ci/tasks/build.sh +++ b/ci/tasks/build.sh @@ -97,8 +97,17 @@ SUDO # Output and checksum the stemcell artifacts # -stemcell_name="bosh-stemcell-${CANDIDATE_BUILD_NUMBER}-${IAAS}-${HYPERVISOR}-${OS_NAME}-${OS_VERSION}${AGENT_SUFFIX}" -meta4_path="${REPO_PARENT}/stemcells-index-output/dev/${OS_NAME}-${OS_VERSION}/${CANDIDATE_BUILD_NUMBER}/${IAAS}-${HYPERVISOR}${AGENT_SUFFIX}.meta4" +# Non-default architectures carry an "-" suffix in the stemcell name +# (see Bosh::Stemcell::Definition#stemcell_name) and get their own index entry, +# so they never collide with the amd64 artifacts. +if [ "${STEMCELL_ARCH}" == "amd64" ]; then + arch_suffix="" +else + arch_suffix="-${STEMCELL_ARCH}" +fi + +stemcell_name="bosh-stemcell-${CANDIDATE_BUILD_NUMBER}-${IAAS}-${HYPERVISOR}-${OS_NAME}-${OS_VERSION}${AGENT_SUFFIX}${arch_suffix}" +meta4_path="${REPO_PARENT}/stemcells-index-output/dev/${OS_NAME}-${OS_VERSION}/${CANDIDATE_BUILD_NUMBER}/${IAAS}-${HYPERVISOR}${AGENT_SUFFIX}${arch_suffix}.meta4" echo "${CANDIDATE_BUILD_NUMBER}" > "${REPO_PARENT}/candidate-build-number/number" mkdir -p "$( dirname "$meta4_path" )" @@ -134,4 +143,4 @@ cd "${REPO_PARENT}/stemcells-index-output" git add -A git config --global user.email "${GIT_USER_EMAIL}" git config --global user.name "${GIT_USER_NAME}" -git commit -m "dev: ${OS_NAME}-${OS_VERSION}/${CANDIDATE_BUILD_NUMBER} (${IAAS}-${HYPERVISOR})" +git commit -m "dev: ${OS_NAME}-${OS_VERSION}/${CANDIDATE_BUILD_NUMBER} (${IAAS}-${HYPERVISOR}${arch_suffix})" diff --git a/stemcell_builder/stages/blobstore_clis/config.sh b/stemcell_builder/stages/blobstore_clis/config.sh index cc1cd4b645..feff8aee06 100755 --- a/stemcell_builder/stages/blobstore_clis/config.sh +++ b/stemcell_builder/stages/blobstore_clis/config.sh @@ -13,10 +13,25 @@ mkdir -p "${downloaded_cli_dir}" blobstore_clis=(bosh-blobstore-dav bosh-blobstore-gcs bosh-blobstore-s3 bosh-blobstore-azure-storage) +# The .url/.sha256sum assets point at linux-amd64 builds. Other +# architectures (e.g. arm64 / Graviton) read .linux-.url/.sha256sum. +# RFC: cloudfoundry/community#1530 +# shellcheck disable=SC2154 +cli_arch="${stemcell_arch:-amd64}" +if [ "${cli_arch}" == "amd64" ]; then + cli_asset_suffix="" +else + cli_asset_suffix=".linux-${cli_arch}" +fi + for cli_binary in "${blobstore_clis[@]}"; do + if [ ! -f "${assets_dir}/${cli_binary}${cli_asset_suffix}.url" ]; then + echo "No linux-${cli_arch} build of ${cli_binary} is configured: expected ${cli_binary}${cli_asset_suffix}.url and .sha256sum in ${assets_dir}" >&2 + exit 1 + fi # shellcheck disable=SC2154 - cli_url=$(cat "${assets_dir}/${cli_binary}.url") - cli_sha256sum=$(cat "${assets_dir}/${cli_binary}.sha256sum") + cli_url=$(cat "${assets_dir}/${cli_binary}${cli_asset_suffix}.url") + cli_sha256sum=$(cat "${assets_dir}/${cli_binary}${cli_asset_suffix}.sha256sum") curl_five_times "${downloaded_cli_dir}/${cli_binary}" "${cli_url}" echo "${cli_sha256sum} ${downloaded_cli_dir}/${cli_binary}" | sha256sum -c - diff --git a/stemcell_builder/stages/bosh_go_agent/apply.sh b/stemcell_builder/stages/bosh_go_agent/apply.sh index ad7938f02a..cbf4b7157a 100755 --- a/stemcell_builder/stages/bosh_go_agent/apply.sh +++ b/stemcell_builder/stages/bosh_go_agent/apply.sh @@ -9,12 +9,27 @@ source $base_dir/lib/prelude_bosh.bash cp -a $assets_dir/alerts.monitrc $chroot/var/vcap/monit/alerts.monitrc cd $assets_dir -wget -O /usr/bin/meta4 https://github.com/dpb587/metalink/releases/download/v0.2.0/meta4-0.2.0-linux-amd64 \ - && echo "81a592eaf647358563f296aced845ac60d9061a45b30b852d1c3f3674720fe19 /usr/bin/meta4" | shasum -a 256 -c \ - && chmod +x /usr/bin/meta4 - +# meta4 runs on the build host. dpb587/metalink publishes a linux-amd64 binary +# only, so on other hosts (e.g. arm64 builders) use the meta4 already provided +# by the os-image-stemcell-builder image. RFC: cloudfoundry/community#1530 +if [ "$(uname -m)" == "x86_64" ]; then + wget -O /usr/bin/meta4 https://github.com/dpb587/metalink/releases/download/v0.2.0/meta4-0.2.0-linux-amd64 \ + && echo "81a592eaf647358563f296aced845ac60d9061a45b30b852d1c3f3674720fe19 /usr/bin/meta4" | shasum -a 256 -c \ + && chmod +x /usr/bin/meta4 + meta4_bin=/usr/bin/meta4 +else + meta4_bin="$(command -v meta4 || true)" + if [ -z "${meta4_bin}" ]; then + echo "meta4 not found on this $(uname -m) build host; the os-image-stemcell-builder image provides it" >&2 + exit 1 + fi +fi + +# Install the agent built for the stemcell's architecture (linux-amd64 or +# linux-arm64); the agent metalink lists one file per architecture. +agent_arch="$(stemcell_target_arch)" bosh_agent_version=$(cat ${assets_dir}/bosh-agent-version) -/usr/bin/meta4 file-download --metalink=${assets_dir}/metalink.meta4 --file=bosh-agent-${bosh_agent_version}-linux-amd64 bosh-agent +${meta4_bin} file-download --metalink=${assets_dir}/metalink.meta4 --file=bosh-agent-${bosh_agent_version}-linux-${agent_arch} bosh-agent mv bosh-agent $chroot/var/vcap/bosh/bin/ ln --force $chroot/var/vcap/bosh/bin/bosh-agent $chroot/var/vcap/bosh/etc/bosh-enable-monit-access diff --git a/stemcell_builder/stages/static_libraries_config/apply.sh b/stemcell_builder/stages/static_libraries_config/apply.sh index 0d3dd4ec75..da5aefffbf 100755 --- a/stemcell_builder/stages/static_libraries_config/apply.sh +++ b/stemcell_builder/stages/static_libraries_config/apply.sh @@ -9,6 +9,12 @@ source $base_dir/etc/settings.bash cp -p "${assets_dir}/static_libraries_list.txt" $chroot/var/vcap/bosh/etc/static_libraries_list +# The list is written with the x86_64 multiarch triplet. On arm64 the same +# libraries live under aarch64-linux-gnu. RFC: cloudfoundry/community#1530 +if [ "$(stemcell_target_arch)" == "arm64" ]; then + sed -i 's/x86_64-linux-gnu/aarch64-linux-gnu/g' $chroot/var/vcap/bosh/etc/static_libraries_list +fi + kernel_suffix="-generic" major_kernel_version="6.8" From 6be1c8a165dc4178f992707acf6b32920065357b Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Mon, 12 Oct 2026 16:29:57 -0700 Subject: [PATCH 05/16] fix: Fail on an unmapped stemcell architecture in the manifest manifest_cloud_properties mapped amd64->x86_64 and arm64->aarch64 but fell back to x86_64 for anything else. Since Definition accepts any architecture string (e.g. stemcell:build[...,riscv64]), that silently mislabeled a non-x86 image as x86_64 in the manifest, which could lead a CPI to boot it on the wrong instance type. Raise ArgumentError for an unmapped architecture instead. amd64 and arm64 behaviour is unchanged. Addresses a CodeRabbit review finding on the ARM64 stemcell PR. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../lib/bosh/stemcell/stemcell_packager.rb | 8 +++++-- .../bosh/stemcell/stemcell_packager_spec.rb | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb index e068865471..d95682d5a6 100644 --- a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb +++ b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb @@ -50,8 +50,12 @@ def manifest(disk_format) def manifest_cloud_properties(disk_format, infrastructure, stemcell_name) # Map the BOSH/Debian architecture to the kernel architecture string - # recorded in the manifest. Defaults to x86_64 for amd64 builds. - architecture = {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(definition.architecture, "x86_64") + # recorded in the manifest. Fail on an unmapped architecture rather than + # mislabeling the image (e.g. defaulting a non-x86 image to x86_64), + # which could lead a CPI to boot it on the wrong instance type. + architecture = {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(definition.architecture) do + raise ArgumentError, "Unsupported stemcell architecture: #{definition.architecture}" + end { "name" => stemcell_name, diff --git a/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb b/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb index 9476dcedf6..6b5e54bbd4 100644 --- a/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb @@ -124,6 +124,29 @@ def additional_cloud_properties }) end + context "when the definition is arm64" do + let(:definition) do + Bosh::Stemcell::Definition.new(infrastructure, "fake_hypervisor", operating_system, "arm64") + end + + it "records aarch64 as the manifest architecture" do + packager.package("raw") + actual_manifest = YAML.load_file(File.join(work_dir, "stemcell/stemcell.MF")) + expect(actual_manifest["cloud_properties"]["architecture"]).to eq("aarch64") + end + end + + context "when the definition has an unsupported architecture" do + let(:definition) do + Bosh::Stemcell::Definition.new(infrastructure, "fake_hypervisor", operating_system, "riscv64") + end + + it "raises rather than mislabeling the manifest architecture" do + expect { packager.package("raw") } + .to raise_error(ArgumentError, /Unsupported stemcell architecture: riscv64/) + end + end + it "returns the path of the created tarball" do expect(packager.package(disk_format)).to eq( File.join(tarball_dir, "bosh-stemcell-1234-fake_infra-fake_hypervisor-ubuntu-noble.tgz") From aff698beaa99baca085f09dd0698b476917c6a3a Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 07:41:38 -0700 Subject: [PATCH 06/16] fix: Use the AWS architecture value in arm64 stemcell manifests The manifest recorded cloud_properties.architecture as "aarch64" for arm64 builds. The AWS CPI passes that value unchanged to EC2 RegisterImage (StemcellCloudProps -> StemcellCreator), and EC2 accepts "arm64" but not "aarch64", so an arm64 AWS stemcell could not be registered as an AMI. Move the mapping into Infrastructure#architecture_name. The default is unchanged (amd64 -> x86_64, arm64 -> aarch64, anything else raises), and Infrastructure::Aws overrides arm64 -> arm64. amd64 manifests are unchanged for every infrastructure. Addresses a review finding on the ARM64 stemcell PR. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../lib/bosh/stemcell/infrastructure.rb | 16 ++++++++ .../lib/bosh/stemcell/stemcell_packager.rb | 8 +--- .../spec/bosh/stemcell/infrastructure_spec.rb | 41 +++++++++++++++++++ .../bosh/stemcell/stemcell_packager_spec.rb | 11 +++++ 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb index be37b1a58e..682a504ba8 100644 --- a/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb +++ b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb @@ -46,6 +46,16 @@ def additional_cloud_properties {} end + # Maps the BOSH/Debian architecture to the value recorded as + # cloud_properties.architecture in stemcell.MF, which CPIs consume + # unchanged. Raises for an unmapped architecture rather than mislabeling + # the image. Infrastructures whose CPI expects another spelling override it. + def architecture_name(architecture) + {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(architecture) do + raise ArgumentError, "Unsupported stemcell architecture: #{architecture}" + end + end + def ==(other) name == other.name && hypervisor == other.hypervisor && @@ -129,6 +139,12 @@ def additional_cloud_properties "boot_mode" => "uefi-preferred" } end + + # The AWS CPI passes the architecture to EC2 RegisterImage unchanged, which + # accepts "arm64" but not "aarch64". + def architecture_name(architecture) + (architecture == "arm64") ? "arm64" : super + end end class Alicloud < Base diff --git a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb index d95682d5a6..298a59672e 100644 --- a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb +++ b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb @@ -49,13 +49,7 @@ def manifest(disk_format) end def manifest_cloud_properties(disk_format, infrastructure, stemcell_name) - # Map the BOSH/Debian architecture to the kernel architecture string - # recorded in the manifest. Fail on an unmapped architecture rather than - # mislabeling the image (e.g. defaulting a non-x86 image to x86_64), - # which could lead a CPI to boot it on the wrong instance type. - architecture = {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(definition.architecture) do - raise ArgumentError, "Unsupported stemcell architecture: #{definition.architecture}" - end + architecture = infrastructure.architecture_name(definition.architecture) { "name" => stemcell_name, diff --git a/bosh-stemcell/spec/bosh/stemcell/infrastructure_spec.rb b/bosh-stemcell/spec/bosh/stemcell/infrastructure_spec.rb index 8e817e456c..d24eafb28b 100644 --- a/bosh-stemcell/spec/bosh/stemcell/infrastructure_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/infrastructure_spec.rb @@ -41,6 +41,31 @@ module Bosh::Stemcell ) expect(infrastructure.additional_cloud_properties).to eq({}) end + + describe "#architecture_name" do + subject(:infrastructure) do + Infrastructure::Base.new( + name: "foo", + hypervisor: "xen", + default_disk_size: 1024, + disk_formats: [], + stemcell_formats: [] + ) + end + + it "maps amd64 to x86_64" do + expect(infrastructure.architecture_name("amd64")).to eq("x86_64") + end + + it "maps arm64 to aarch64" do + expect(infrastructure.architecture_name("arm64")).to eq("aarch64") + end + + it "raises for an unsupported architecture rather than mislabeling it" do + expect { infrastructure.architecture_name("riscv64") } + .to raise_error(ArgumentError, /Unsupported stemcell architecture: riscv64/) + end + end end describe Infrastructure::NullInfrastructure do @@ -91,6 +116,22 @@ module Bosh::Stemcell it "has aws specific additional cloud properties" do expect(subject.additional_cloud_properties).to eq({"root_device_name" => "/dev/sda1", "boot_mode" => "uefi-preferred"}) end + + describe "#architecture_name" do + it "maps amd64 to x86_64" do + expect(subject.architecture_name("amd64")).to eq("x86_64") + end + + # EC2 RegisterImage accepts "arm64" but not "aarch64". + it "maps arm64 to arm64" do + expect(subject.architecture_name("arm64")).to eq("arm64") + end + + it "raises for an unsupported architecture" do + expect { subject.architecture_name("riscv64") } + .to raise_error(ArgumentError, /Unsupported stemcell architecture: riscv64/) + end + end end describe Infrastructure::Alicloud do diff --git a/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb b/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb index 6b5e54bbd4..05d7d0d049 100644 --- a/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb @@ -134,6 +134,17 @@ def additional_cloud_properties actual_manifest = YAML.load_file(File.join(work_dir, "stemcell/stemcell.MF")) expect(actual_manifest["cloud_properties"]["architecture"]).to eq("aarch64") end + + context "on AWS" do + let(:infrastructure) { Bosh::Stemcell::Infrastructure.for("aws") } + + # The AWS CPI forwards this value to EC2 RegisterImage, which rejects "aarch64". + it "records arm64 as the manifest architecture" do + packager.package("raw") + actual_manifest = YAML.load_file(File.join(work_dir, "stemcell/stemcell.MF")) + expect(actual_manifest["cloud_properties"]["architecture"]).to eq("arm64") + end + end end context "when the definition has an unsupported architecture" do From b5e9dff87ea9a670391f2059aeb2f43be93b9af9 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 07:53:21 -0700 Subject: [PATCH 07/16] fix(ci): Run amd64-only resource steps off the arm64 workers The dpb587/metalink-repository-resource and frodenas/gcs-resource images are published for linux/amd64 only. A resource step runs its container on the worker its tags select, so tagging these steps arm64 would fail with an exec format error on a native arm64 worker. Drop the arm64 tag from the five steps that use them (usn-log, stemcell-trigger, os-image-tarball-arm64 get/put, aws-xen-hvm-arm64 put). They only fetch or publish files, so they can run on the existing workers; the task steps stay on arm64 and receive the artifacts as volumes streamed between workers. Checked by rendering the pipeline: no arm64-tagged step uses a custom resource type, every arm64 task step is still arm64-tagged, and the amd64 jobs are unchanged. Not exercised on a live Concourse. Addresses a review finding on the ARM64 stemcell PR. sim: https://github.com/cloudfoundry/community/pull/1530 --- ci/pipeline-template.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index 28e0f9b47a..2b8a3cd19c 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -529,10 +529,8 @@ jobs: bump: "major" tags: [ arm64 ] - get: usn-log - tags: [ arm64 ] - get: stemcell-trigger trigger: true - tags: [ arm64 ] - task: build file: bosh-stemcells-ci/ci/tasks/os-images/build.yml image: os-image-stemcell-builder-registry-image-arm64 @@ -546,7 +544,6 @@ jobs: vars: image_os_tag: (@= data.values.stemcell_details.os_short_name @) - put: os-image-tarball-arm64 - tags: [ arm64 ] params: files: - os-image/(@= data.values.stemcell_details.os_name @).tgz @@ -793,7 +790,6 @@ jobs: resource: os-image-tarball-arm64 passed: - build-os-image-arm64 - tags: [ arm64 ] - task: create-stemcell file: bosh-stemcells-ci/ci/tasks/build.yml image: os-image-stemcell-builder-registry-image-arm64 @@ -814,7 +810,6 @@ jobs: image_os_tag: (@= data.values.stemcell_details.os_short_name @) - put: aws-xen-hvm-arm64 attempts: 3 - tags: [ arm64 ] params: files: - stemcell/*.tgz From 93b1b868bcfe73290d8d0b20d175874249767852 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 07:58:32 -0700 Subject: [PATCH 08/16] fix(ci): Find and name the raw stemcell archive with the arch suffix Definition#stemcell_name appends the architecture after the disk format, so an arm64 raw archive is ...-raw-arm64.tgz. build.sh still globbed *-raw.tgz, so the raw archive was silently left out of the task output and the metalink, and it composed the published name with the architecture before "raw". Include the architecture suffix in the glob, move the file that was matched, and compose the published name as -raw.tgz to match the Ruby naming. With an empty suffix (amd64) the glob, the moved file and the published name are identical to before. Addresses a review finding on the ARM64 stemcell PR. sim: https://github.com/cloudfoundry/community/pull/1530 --- ci/tasks/build.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ci/tasks/build.sh b/ci/tasks/build.sh index ac26e616b6..191dc4a68d 100755 --- a/ci/tasks/build.sh +++ b/ci/tasks/build.sh @@ -106,7 +106,8 @@ else arch_suffix="-${STEMCELL_ARCH}" fi -stemcell_name="bosh-stemcell-${CANDIDATE_BUILD_NUMBER}-${IAAS}-${HYPERVISOR}-${OS_NAME}-${OS_VERSION}${AGENT_SUFFIX}${arch_suffix}" +stemcell_name_base="bosh-stemcell-${CANDIDATE_BUILD_NUMBER}-${IAAS}-${HYPERVISOR}-${OS_NAME}-${OS_VERSION}${AGENT_SUFFIX}" +stemcell_name="${stemcell_name_base}${arch_suffix}" meta4_path="${REPO_PARENT}/stemcells-index-output/dev/${OS_NAME}-${OS_VERSION}/${CANDIDATE_BUILD_NUMBER}/${IAAS}-${HYPERVISOR}${AGENT_SUFFIX}${arch_suffix}.meta4" echo "${CANDIDATE_BUILD_NUMBER}" > "${REPO_PARENT}/candidate-build-number/number" @@ -114,7 +115,7 @@ mkdir -p "$( dirname "$meta4_path" )" rm -f "$meta4_path" meta4 create --metalink="$meta4_path" -raw_images=( "${REPO_PARENT}/bosh-linux-stemcell-builder/tmp"/*-raw.tgz ) +raw_images=( "${REPO_PARENT}/bosh-linux-stemcell-builder/tmp"/*-raw${arch_suffix}.tgz ) if [ "${#raw_images[@]}" -ge 2 ]; then echo "Found more than one raw image: '${raw_images[*]}'" >&2 exit 1 @@ -122,8 +123,8 @@ fi if [ -e "${raw_images[0]}" ] ; then # openstack currently publishes raw files - raw_stemcell_filename="${stemcell_name}-raw.tgz" - mv "${REPO_PARENT}/bosh-linux-stemcell-builder/tmp"/*-raw.tgz "${REPO_PARENT}/stemcell/${raw_stemcell_filename}" + raw_stemcell_filename="${stemcell_name_base}-raw${arch_suffix}.tgz" + mv "${raw_images[0]}" "${REPO_PARENT}/stemcell/${raw_stemcell_filename}" meta4 import-file --metalink="$meta4_path" --version="${CANDIDATE_BUILD_NUMBER}" "${REPO_PARENT}/stemcell/${raw_stemcell_filename}" meta4 file-set-url --metalink="$meta4_path" --file="${raw_stemcell_filename}" "https://${S3_API_ENDPOINT}/${STEMCELL_BUCKET}/${IAAS}/${raw_stemcell_filename}" From 067ddc2face99a6f59ee96f8c974ca3860daa801 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 09:07:04 -0700 Subject: [PATCH 09/16] refactor(spec): Share one apt sources regex across architectures The base_apt spec branched on the host architecture and repeated three near-identical regexes per branch. Use one regex that accepts the archive, ports, security and snapshot hosts, with the optional -ports path, and assert it once per listing (noble, noble-updates, noble-security). Renaming a listing now means changing one place. The pattern still rejects other hosts, other suites and look-alike paths, and accepts every line the previous amd64 and arm64 patterns accepted; checked against sample sources.list contents for amd64, arm64 and snapshot mirrors. The spec only runs against a built image, so it was not run end to end here. sim: https://github.com/cloudfoundry/community/pull/1530 --- bosh-stemcell/spec/os_image/ubuntu_spec.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bosh-stemcell/spec/os_image/ubuntu_spec.rb b/bosh-stemcell/spec/os_image/ubuntu_spec.rb index dd63711793..e45d543b23 100644 --- a/bosh-stemcell/spec/os_image/ubuntu_spec.rb +++ b/bosh-stemcell/spec/os_image/ubuntu_spec.rb @@ -33,15 +33,9 @@ describe "base_apt" do describe file("/etc/apt/sources.list") do - if Bosh::Stemcell::Arch.x86_64? - its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble main universe multiverse' } - its(:content) { should match 'deb http:\/\/(archive|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-updates main universe multiverse' } - its(:content) { should match 'deb http:\/\/(security|snapshot).ubuntu.com\/ubuntu(|\/\d*T\d*Z) noble-security main universe multiverse' } - else - # ARM64 (and other non-amd64 arches) are served from the ports mirror. - its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble main universe multiverse' } - its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble-updates main universe multiverse' } - its(:content) { should match 'deb http:\/\/(ports|snapshot).ubuntu.com\/ubuntu(-ports)?(|\/\d*T\d*Z) noble-security main universe multiverse' } + # ports.ubuntu.com (ubuntu-ports) serves the non-amd64 architectures. + %w[noble noble-updates noble-security].each do |listing| + its(:content) { should match %r{deb http://(archive|ports|security|snapshot)\.ubuntu\.com/ubuntu(-ports)?(|/\d*T\d*Z) #{listing} main universe multiverse} } end end From 0c7c0286a642b4e638896c88527dbff3f5d5e483 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 09:16:56 -0700 Subject: [PATCH 10/16] chore: Trim status narration from comments in the arm64 changes Comments that describe project status (RFC links, "dormant until...", "existing builds are unaffected") go stale and make merging forward across the supported version lines harder. The rationale is in the commit messages of the changes they described. - pipeline-template.yml: remove the comment blocks above the arm64 jobs and resources. - Elsewhere keep only comments that explain a lasting "why" (for example why ports.ubuntu.com is used and why ovftool is amd64-only), shortened and without RFC references. Comment lines only: with comment lines stripped, every changed file is identical to before, and the rendered pipeline is byte-identical. sim: https://github.com/cloudfoundry/community/pull/1530 --- bosh-stemcell/lib/bosh/stemcell/definition.rb | 4 +- .../lib/bosh/stemcell/infrastructure.rb | 5 +-- .../os-image-stemcell-builder/Dockerfile | 12 ++---- ci/pipeline-template.yml | 39 ------------------- ci/tasks/build-docker-args.sh | 3 +- stemcell_builder/lib/helpers.sh | 5 +-- stemcell_builder/stages/base_apt/apply.sh | 3 -- .../stages/base_debootstrap/apply.sh | 6 +-- .../stages/base_debootstrap/config.sh | 4 +- .../stages/blobstore_clis/config.sh | 3 +- stemcell_builder/stages/system_grub/apply.sh | 3 +- 11 files changed, 14 insertions(+), 73 deletions(-) diff --git a/bosh-stemcell/lib/bosh/stemcell/definition.rb b/bosh-stemcell/lib/bosh/stemcell/definition.rb index 3dbd25f77e..26e972166c 100644 --- a/bosh-stemcell/lib/bosh/stemcell/definition.rb +++ b/bosh-stemcell/lib/bosh/stemcell/definition.rb @@ -3,7 +3,6 @@ module Bosh::Stemcell class Definition - # Architecture defaults to amd64 so existing stemcell builds are unaffected. DEFAULT_ARCHITECTURE = "amd64".freeze attr_reader :infrastructure, :hypervisor_name, :operating_system, :architecture @@ -33,8 +32,7 @@ def stemcell_name(disk_format) stemcell_name_parts << operating_system.version if operating_system.version stemcell_name_parts << operating_system.variant if operating_system.variant stemcell_name_parts << disk_format unless disk_format == infrastructure.default_disk_format - # Only embed the architecture in the name for non-default (arm64) builds so - # existing amd64 stemcell names remain byte-identical. + # amd64 stemcell names carry no architecture suffix; other architectures do. stemcell_name_parts << architecture unless architecture == DEFAULT_ARCHITECTURE stemcell_name_parts.join("-") diff --git a/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb index 682a504ba8..72d0234425 100644 --- a/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb +++ b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb @@ -47,9 +47,8 @@ def additional_cloud_properties end # Maps the BOSH/Debian architecture to the value recorded as - # cloud_properties.architecture in stemcell.MF, which CPIs consume - # unchanged. Raises for an unmapped architecture rather than mislabeling - # the image. Infrastructures whose CPI expects another spelling override it. + # cloud_properties.architecture in stemcell.MF, which CPIs consume unchanged. + # Raises for an unmapped architecture; override where a CPI expects another spelling. def architecture_name(architecture) {"amd64" => "x86_64", "arm64" => "aarch64"}.fetch(architecture) do raise ArgumentError, "Unsupported stemcell architecture: #{architecture}" diff --git a/ci/docker/os-image-stemcell-builder/Dockerfile b/ci/docker/os-image-stemcell-builder/Dockerfile index 42e228223c..4d2afc7628 100644 --- a/ci/docker/os-image-stemcell-builder/Dockerfile +++ b/ci/docker/os-image-stemcell-builder/Dockerfile @@ -76,11 +76,7 @@ RUN mkdir -p /mnt/stemcells \ && chown -R ubuntu:ubuntu /mnt/stemcells # VMware's ovftool is used to create vSphere stemcells. VMware ships no arm64 -# Linux ovftool, and vSphere is not an arm64 (Graviton) target, so the ovftool -# bundle is only added and installed on amd64. On arm64 the ADD is skipped -# entirely (a conditional build stage), which keeps the arm64 image buildable -# without an ovftool bundle in the build context. -# RFC: cloudfoundry/community#1530. +# Linux ovftool and vSphere is not an arm64 target, so it is only installed on amd64. # # TARGETARCH is provided automatically by BuildKit ("amd64" / "arm64"). ARG TARGETARCH @@ -111,10 +107,8 @@ ARG RUBY_INSTALL_URL ARG RUBY_VERSION ARG GEM_HOME -# Use the prebuilt meta4 release binary when one exists for this architecture -# (amd64). dpb587/metalink publishes no arm64 binary, so on arm64 -# build-docker-args.sh leaves META4_CLI_URL empty and meta4 is built from source -# with the image's Go toolchain instead. +# dpb587/metalink publishes no arm64 binary, so on arm64 build-docker-args.sh +# leaves META4_CLI_URL empty and meta4 is built from source with the image's Go toolchain. RUN meta4_cli_path="/usr/local/bin/meta4" \ && if [ -n "${META4_CLI_URL}" ]; then \ curl --show-error -sL "${META4_CLI_URL}" > "${meta4_cli_path}"; \ diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index 2b8a3cd19c..ccb26c406c 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -250,13 +250,6 @@ jobs: params: image: image/image.tar -#! arm64 (Graviton) builder image. RFC: cloudfoundry/community#1530. -#! Mirrors build-os-image-stemcell-builder on an arm64 worker so build-os-image-arm64 -#! runs in a native arm64 image. The Dockerfile skips VMware ovftool on arm64 (no -#! arm64 ovftool exists; vSphere is not an arm64 target), and build-docker-args.sh -#! selects arm64 CLI assets from the worker's architecture. Pushes to a separate -#! "-arm64" tag so the amd64 image is never overwritten. Dormant until an -#! `arm64`-tagged worker is registered (RFC D3 / Open Question 2). - name: build-os-image-stemcell-builder-arm64 public: true serial: true @@ -499,19 +492,6 @@ jobs: params: file: os-image-version/number -#! arm64 (Graviton) OS image build. RFC: cloudfoundry/community#1530. -#! Mirrors build-os-image but runs the same ci/tasks/os-images/build.yml task on -#! an arm64 worker, inside the arm64 builder image. STEMCELL_ARCH=arm64 selects -#! the target architecture (passed to the rake task -> debootstrap --arch), and -#! this job supplies the ports.ubuntu.com mirror that hosts arm64 Ubuntu -#! packages. Later stages read the arch back from the rootfs -#! (stemcell_target_arch). It publishes to its own os-image-tarball-arm64 -#! resource and version series so it never perturbs the amd64 os-image artifacts. -#! Dormant until the platform team registers an `arm64`-tagged Concourse worker -#! (RFC D3 / Open Question 2). Heavy/light stemcell packaging + arm64 AMI/GCE -#! publishing for downstream IaaSes is platform-owned (RFC D5) and tracked -#! separately; this job covers the os-image stage whose arch-correctness lives -#! in this repo (validated 376/0 on a Graviton instance). - name: build-os-image-arm64 serial: true plan: @@ -750,16 +730,6 @@ jobs: - #@ build_stemcell(iaas.iaas, iaas.hypervisor, "-fips") #@ end -#! arm64 (Graviton) AWS stemcell. RFC: cloudfoundry/community#1530. -#! Mirrors build-aws-xen-hvm on an arm64 worker: it builds the stemcell from the -#! arm64 os-image (build-os-image-arm64) inside the arm64 builder image with -#! STEMCELL_ARCH=arm64. The stemcell is named ...-ubuntu--arm64 and indexed -#! under aws-xen-hvm-arm64.meta4, so it never collides with the amd64 stemcell. -#! It is not an input to test-stemcells-*, bats or aggregate-candidate-stemcells. -#! Dormant until an `arm64`-tagged worker is registered (RFC D3 / Open Question -#! 2). It also needs linux-arm64 builds of the bosh-agent (listed in the agent -#! metalink) and of the four blobstore CLIs (bosh-blobstore-*.linux-arm64.url / -#! .sha256sum assets in stemcell_builder/stages/blobstore_clis). - name: build-aws-xen-hvm-arm64 serial: true plan: @@ -1827,8 +1797,6 @@ resources: - #@ metalink_resource(iaas.iaas, iaas.hypervisor, "-fips") #@ end -#! arm64 (Graviton) AWS stemcell dev index entry (aws-xen-hvm-arm64.meta4), -#! published by build-aws-xen-hvm-arm64. RFC: cloudfoundry/community#1530. - name: aws-xen-hvm-arm64 type: metalink-repository source: @@ -1865,9 +1833,6 @@ resources: secret_key: ((hmac_secret)) type: s3 -#! arm64 (Graviton) OS image tarball. RFC: cloudfoundry/community#1530. -#! Mirrors os-image-tarball but under a distinct -arm64 meta4 name and S3 prefix -#! so the arm64 os-image never overwrites the amd64 one. - name: os-image-tarball-arm64 type: metalink-repository source: @@ -1904,8 +1869,6 @@ resources: file: (@= data.values.stemcell_details.branch @)/os-image-version private_key: ((github_deploy_key_bosh-linux-stemcell-builder.private_key)) -#! arm64 (Graviton) os-image version series, independent of the amd64 series. -#! RFC: cloudfoundry/community#1530. - name: os-image-version-arm64 type: semver source: @@ -2106,8 +2069,6 @@ resources: username: ((dockerhub_username)) password: ((dockerhub_password)) -#! arm64 (Graviton) builder image, published under its own tag so the amd64 -#! image is never overwritten. RFC: cloudfoundry/community#1530. - name: os-image-stemcell-builder-registry-image-arm64 type: registry-image source: diff --git a/ci/tasks/build-docker-args.sh b/ci/tasks/build-docker-args.sh index cdf9012334..b1e90f3a91 100755 --- a/ci/tasks/build-docker-args.sh +++ b/ci/tasks/build-docker-args.sh @@ -19,8 +19,7 @@ apt-get update -y apt-get install -y --no-install-recommends ca-certificates curl jq # Select CLI assets for the architecture this task runs on, so the builder image -# can be built for both amd64 and arm64 (Graviton) workers. -# RFC: https://github.com/cloudfoundry/community/pull/1530 +# can be built for both amd64 and arm64 workers. case "$(uname -m)" in aarch64|arm64) arch="arm64" ;; *) arch="amd64" ;; diff --git a/stemcell_builder/lib/helpers.sh b/stemcell_builder/lib/helpers.sh index ad5174cde6..80c44f63a8 100644 --- a/stemcell_builder/lib/helpers.sh +++ b/stemcell_builder/lib/helpers.sh @@ -96,9 +96,8 @@ function is_x86_64() { # Prints the Debian architecture (e.g. amd64, arm64) of the stemcell being built. # Prefers the architecture of the bootstrapped root filesystem (set by -# debootstrap), which is reliably available to every stage regardless of how -# build settings are propagated. Falls back to the stemcell_arch build setting, -# then to amd64 so existing builds are unaffected. +# debootstrap), which is available to every stage regardless of how build +# settings are propagated. Falls back to the stemcell_arch build setting, then amd64. # # Usage: stemcell_target_arch [rootfs_path] # rootfs_path defaults to $chroot; image stages that mount the rootfs diff --git a/stemcell_builder/stages/base_apt/apply.sh b/stemcell_builder/stages/base_apt/apply.sh index f7dcf6c109..29b84d111c 100755 --- a/stemcell_builder/stages/base_apt/apply.sh +++ b/stemcell_builder/stages/base_apt/apply.sh @@ -10,9 +10,6 @@ add_on_exit "umount $chroot/sys" # Ubuntu serves non-amd64 architectures (e.g. arm64) from the ports mirror # (ports.ubuntu.com/ubuntu-ports) rather than archive/security.ubuntu.com. -# stemcell_target_arch derives the arch from the bootstrapped chroot, so this -# works regardless of how build settings are propagated. Defaults to the amd64 -# mirrors for amd64/unknown so existing builds are unchanged. if [ "$(stemcell_target_arch)" == "arm64" ]; then apt_archive_mirror="http://ports.ubuntu.com/ubuntu-ports" apt_security_mirror="http://ports.ubuntu.com/ubuntu-ports" diff --git a/stemcell_builder/stages/base_debootstrap/apply.sh b/stemcell_builder/stages/base_debootstrap/apply.sh index 25cd0f38c8..a0c6fa8393 100755 --- a/stemcell_builder/stages/base_debootstrap/apply.sh +++ b/stemcell_builder/stages/base_debootstrap/apply.sh @@ -76,10 +76,8 @@ cleanup_debootstrap() { } trap cleanup_debootstrap EXIT -# archive.ubuntu.com only serves amd64/i386; non-amd64 arches (e.g. arm64) are -# served from the ports mirror. Default arm64 to ports.ubuntu.com when no -# explicit mirror was provided, so ARM64 builds work without extra config. -# An explicit UBUNTU_DEBOOTSTRAP_MIRROR always takes precedence. +# archive.ubuntu.com only serves amd64/i386; other architectures (e.g. arm64) are +# served from the ports mirror. An explicit UBUNTU_DEBOOTSTRAP_MIRROR takes precedence. debootstrap_mirror="${UBUNTU_DEBOOTSTRAP_MIRROR:-}" if [ -z "$debootstrap_mirror" ] && [ "$base_debootstrap_arch" == "arm64" ]; then debootstrap_mirror="http://ports.ubuntu.com/ubuntu-ports" diff --git a/stemcell_builder/stages/base_debootstrap/config.sh b/stemcell_builder/stages/base_debootstrap/config.sh index cdaef5571b..0ff353ef1b 100755 --- a/stemcell_builder/stages/base_debootstrap/config.sh +++ b/stemcell_builder/stages/base_debootstrap/config.sh @@ -18,9 +18,7 @@ then persist UBUNTU_DEBOOTSTRAP_MIRROR fi -# Architecture of the stemcell being built. Defaults to amd64 so existing -# builds are unaffected; set stemcell_arch=arm64 (via settings.bash) to build -# an ARM64 stemcell. +# Architecture of the stemcell being built, from the stemcell_arch build setting. base_debootstrap_arch="${stemcell_arch:-amd64}" if [ -z "${base_debootstrap_suite:-}" ] diff --git a/stemcell_builder/stages/blobstore_clis/config.sh b/stemcell_builder/stages/blobstore_clis/config.sh index feff8aee06..9a1c8a2cd4 100755 --- a/stemcell_builder/stages/blobstore_clis/config.sh +++ b/stemcell_builder/stages/blobstore_clis/config.sh @@ -14,8 +14,7 @@ mkdir -p "${downloaded_cli_dir}" blobstore_clis=(bosh-blobstore-dav bosh-blobstore-gcs bosh-blobstore-s3 bosh-blobstore-azure-storage) # The .url/.sha256sum assets point at linux-amd64 builds. Other -# architectures (e.g. arm64 / Graviton) read .linux-.url/.sha256sum. -# RFC: cloudfoundry/community#1530 +# architectures (e.g. arm64) read .linux-.url/.sha256sum. # shellcheck disable=SC2154 cli_arch="${stemcell_arch:-amd64}" if [ "${cli_arch}" == "amd64" ]; then diff --git a/stemcell_builder/stages/system_grub/apply.sh b/stemcell_builder/stages/system_grub/apply.sh index 9227003ad4..82f2b2748f 100755 --- a/stemcell_builder/stages/system_grub/apply.sh +++ b/stemcell_builder/stages/system_grub/apply.sh @@ -5,8 +5,7 @@ set -e base_dir=$(readlink -nf $(dirname $0)/../..) source $base_dir/lib/prelude_apply.bash -# Install the GRUB EFI packages matching the target architecture. Defaults to -# amd64 so existing builds are unchanged; arm64 uses the arm64 EFI packages. +# Install the GRUB EFI packages matching the target architecture. if [ "$(stemcell_target_arch)" == "arm64" ]; then # ARM64 has no "grub2" metapackage; grub-efi-arm64 is the equivalent. pkg_mgr install grub-efi-arm64 grub-efi-arm64-bin grub-efi-arm64-signed shim-signed From f683baa9222af735560376014b73c50e456c9c63 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 09:20:30 -0700 Subject: [PATCH 11/16] refactor(ci): Require STEMCELL_ARCH in the stemcell build tasks The build and os-image tasks defaulted STEMCELL_ARCH to amd64, which hides that the builder is a multi-architecture tool. Make it a required input, following the convention the tasks already use for IAAS, OS_NAME and the like: the task params default to replace-me and the scripts reject that value with check_param. The amd64 jobs in the pipeline now pass STEMCELL_ARCH: amd64 explicitly (build-os-image and the per-IaaS stemcell builds); the arm64 jobs already did. amd64 stemcell names and artifact paths are unchanged: the "-" suffix is still only added for other architectures. Checked by rendering the pipeline: every task that uses these two files passes STEMCELL_ARCH, the only rendered difference in existing jobs is that added param, and the amd64 names, metalink paths and raw archive handling match the previous scripts. sim: https://github.com/cloudfoundry/community/pull/1530 --- ci/pipeline-template.yml | 2 ++ ci/tasks/build.sh | 5 +---- ci/tasks/build.yml | 2 +- ci/tasks/os-images/build.sh | 5 +---- ci/tasks/os-images/build.yml | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/ci/pipeline-template.yml b/ci/pipeline-template.yml index ccb26c406c..d9ea5fd8c8 100644 --- a/ci/pipeline-template.yml +++ b/ci/pipeline-template.yml @@ -59,6 +59,7 @@ plan: OS_NAME: ubuntu S3_API_ENDPOINT: storage.googleapis.com OS_VERSION: (@= data.values.stemcell_details.os_short_name @)(@= FIPS @) + STEMCELL_ARCH: amd64 STEMCELL_BUCKET: bosh-core-stemcells-candidate(@= FIPS @) GIT_USER_EMAIL: (@= data.values.stemcell_details.bot_email @) GIT_USER_NAME: (@= data.values.stemcell_details.bot_name @) @@ -473,6 +474,7 @@ jobs: params: OPERATING_SYSTEM_NAME: ubuntu OPERATING_SYSTEM_VERSION: (@= data.values.stemcell_details.os_short_name @) + STEMCELL_ARCH: amd64 UBUNTU_DEBOOTSTRAP_MIRROR: http://mirrors.edge.kernel.org/ubuntu privileged: true vars: diff --git a/ci/tasks/build.sh b/ci/tasks/build.sh index 191dc4a68d..9611b1a5f0 100755 --- a/ci/tasks/build.sh +++ b/ci/tasks/build.sh @@ -23,6 +23,7 @@ check_param IAAS check_param HYPERVISOR check_param OS_NAME check_param OS_VERSION +check_param STEMCELL_ARCH OS_IMAGE="$(readlink -f "${REPO_PARENT}/os-image-tarball"/*.tgz)" @@ -77,10 +78,6 @@ chown -R ubuntu:ubuntu "${REPO_PARENT}/bosh-linux-stemcell-builder" chown -R ubuntu:ubuntu /mnt sudo chmod u+s "$(which sudo)" -# Architecture of the stemcell to build. Defaults to amd64 so existing pipelines -# are unaffected; set STEMCELL_ARCH=arm64 to build an ARM64 stemcell. -STEMCELL_ARCH="${STEMCELL_ARCH:-amd64}" - # pass SHLVL or '~ubuntu/.bash_logout' will exit 1 sudo --set-home --user ubuntu \ --preserve-env=GEM_HOME,SHLVL,UBUNTU_ADVANTAGE_TOKEN,UBUNTU_FIPS_USE_IAAS_KERNEL \ diff --git a/ci/tasks/build.yml b/ci/tasks/build.yml index aef737e2e7..d8b8eb08c6 100644 --- a/ci/tasks/build.yml +++ b/ci/tasks/build.yml @@ -23,7 +23,7 @@ params: HYPERVISOR: replace-me OS_NAME: replace-me OS_VERSION: replace-me - STEMCELL_ARCH: amd64 + STEMCELL_ARCH: replace-me STEMCELL_BUCKET: replace-me S3_API_ENDPOINT: s3.amazonaws.com ESM_TOKEN: diff --git a/ci/tasks/os-images/build.sh b/ci/tasks/os-images/build.sh index 344e6c0635..a530ea024c 100755 --- a/ci/tasks/os-images/build.sh +++ b/ci/tasks/os-images/build.sh @@ -21,6 +21,7 @@ function check_param() { check_param OPERATING_SYSTEM_NAME check_param OPERATING_SYSTEM_VERSION +check_param STEMCELL_ARCH OS_IMAGE_NAME=${OPERATING_SYSTEM_NAME}-${OPERATING_SYSTEM_VERSION} OS_IMAGE=${REPO_PARENT}/os-image/${OS_IMAGE_NAME}.tgz @@ -35,10 +36,6 @@ chown -R ubuntu:ubuntu "${REPO_PARENT}/bosh-linux-stemcell-builder" chown -R ubuntu:ubuntu /mnt sudo chmod u+s "$(which sudo)" -# Architecture of the OS image to build. Defaults to amd64 so existing pipelines -# are unaffected; set STEMCELL_ARCH=arm64 to build an ARM64 OS image. -STEMCELL_ARCH="${STEMCELL_ARCH:-amd64}" - # pass SHLVL or '~ubuntu/.bash_logout' will exit 1 sudo --set-home --user ubuntu \ --preserve-env=GEM_HOME,SHLVL,BUILD_TIME,UBUNTU_ADVANTAGE_TOKEN,UBUNTU_DEBOOTSTRAP_MIRROR \ diff --git a/ci/tasks/os-images/build.yml b/ci/tasks/os-images/build.yml index 4405744385..61ae469cd3 100644 --- a/ci/tasks/os-images/build.yml +++ b/ci/tasks/os-images/build.yml @@ -17,7 +17,7 @@ run: params: OPERATING_SYSTEM_NAME: replace-me OPERATING_SYSTEM_VERSION: replace-me - STEMCELL_ARCH: amd64 + STEMCELL_ARCH: replace-me ESM_TOKEN: UBUNTU_ADVANTAGE_TOKEN: UBUNTU_DEBOOTSTRAP_MIRROR: From a7ecfabd53265aadbfc87a7b804730123d0005de Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Fri, 30 Oct 2026 09:28:12 -0700 Subject: [PATCH 12/16] fix(spec): Skip the /lib64 ownership check on arm64 with an rspec tag The arm64 change replaced the strict /lib64 ownership check with a command that echoes "root" when /lib64 is missing, so the spec could silently pass on amd64 where the directory must exist. Restore the original check and tag it exclude_on_arm64 (arm64 has no /lib64; it uses /lib/ld-linux-aarch64.so.1). The stemcell rspec command now adds --tag ~exclude_on_ alongside the existing infrastructure and variant filters, so arch-specific examples are selected by tag instead of by conditionals inside the spec. The tag is emitted for every architecture, including amd64, so an exclude_on_amd64 tag is honored rather than silently ignored; no example uses it yet. Also squeeze the separators in the generated filter string now that it can carry three tags. Checked with real rspec: the tagged example runs under the amd64 filter and is skipped under the arm64 filter. sim: https://github.com/cloudfoundry/community/pull/1530 --- .../lib/bosh/stemcell/build_environment.rb | 3 +- .../bosh/stemcell/build_environment_spec.rb | 41 +++++++++++++++++-- .../spec/support/stemcell_shared_examples.rb | 8 ++-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/bosh-stemcell/lib/bosh/stemcell/build_environment.rb b/bosh-stemcell/lib/bosh/stemcell/build_environment.rb index 307c39645f..439cac7ec2 100644 --- a/bosh-stemcell/lib/bosh/stemcell/build_environment.rb +++ b/bosh-stemcell/lib/bosh/stemcell/build_environment.rb @@ -193,10 +193,11 @@ def exclude_exclusions when Infrastructure::Gdch::NAME " --tag ~exclude_on_gdch" end, + " --tag ~exclude_on_#{definition.architecture}", if operating_system.variant? " --tag ~exclude_on_#{operating_system.variant}" end - ].compact.join(" ").rstrip + ].compact.join(" ").squeeze(" ").rstrip end def image_file_path diff --git a/bosh-stemcell/spec/bosh/stemcell/build_environment_spec.rb b/bosh-stemcell/spec/bosh/stemcell/build_environment_spec.rb index 8ea3a922d1..475be7849c 100644 --- a/bosh-stemcell/spec/bosh/stemcell/build_environment_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/build_environment_spec.rb @@ -17,7 +17,8 @@ module Bosh::Stemcell instance_double( "Bosh::Stemcell::Definition", infrastructure: infrastructure, - operating_system: operating_system + operating_system: operating_system, + architecture: "amd64" ) end @@ -211,7 +212,7 @@ module Bosh::Stemcell "OS_NAME=#{operating_system.name}", "OS_VERSION=#{operating_system.version}", "CANDIDATE_BUILD_NUMBER=#{version}", - "bundle exec rspec -fd --tag ~exclude_on_fips", + "bundle exec rspec -fd --tag ~exclude_on_amd64 --tag ~exclude_on_fips", "spec/os_image/#{operating_system.name}_spec.rb", "spec/stemcells/#{operating_system.name}_spec.rb", "spec/stemcells/go_agent_spec.rb", @@ -243,7 +244,7 @@ module Bosh::Stemcell "OS_NAME=#{operating_system.name}", "OS_VERSION=#{operating_system.version}", "CANDIDATE_BUILD_NUMBER=#{version}", - "bundle exec rspec -fd --tag ~exclude_on_gdch", + "bundle exec rspec -fd --tag ~exclude_on_gdch --tag ~exclude_on_amd64", "spec/os_image/#{operating_system.name}_spec.rb", "spec/stemcells/#{operating_system.name}_spec.rb", "spec/stemcells/go_agent_spec.rb", @@ -255,6 +256,40 @@ module Bosh::Stemcell expect(subject.stemcell_rspec_command).to eq(expected_rspec_command) end end + + context "when the architecture is arm64" do + let(:definition) do + instance_double( + "Bosh::Stemcell::Definition", + infrastructure: infrastructure, + operating_system: operating_system, + architecture: "arm64" + ) + end + + before { allow(operating_system).to receive(:variant?).and_return(false) } + + it "excludes arm64 tagged specs instead of amd64 tagged specs" do + expected_rspec_command = [ + "cd #{stemcell_specs_dir};", + "STEMCELL_IMAGE=#{File.join(work_path, "fake-root-disk-image.raw")}", + "STEMCELL_WORKDIR=#{work_path}", + "STEMCELL_INFRASTRUCTURE=#{infrastructure.name}", + "OS_NAME=#{operating_system.name}", + "OS_VERSION=#{operating_system.version}", + "CANDIDATE_BUILD_NUMBER=#{version}", + "bundle exec rspec -fd --tag ~exclude_on_arm64", + "spec/os_image/#{operating_system.name}_spec.rb", + "spec/stemcells/#{operating_system.name}_spec.rb", + "spec/stemcells/go_agent_spec.rb", + "spec/stemcells/#{infrastructure.name}_spec.rb", + "spec/stemcells/stig_spec.rb", + "spec/stemcells/cis_spec.rb" + ].join(" ") + + expect(subject.stemcell_rspec_command).to eq(expected_rspec_command) + end + end end describe "#build_path" do diff --git a/bosh-stemcell/spec/support/stemcell_shared_examples.rb b/bosh-stemcell/spec/support/stemcell_shared_examples.rb index 628430c73b..f37c2e7dc2 100644 --- a/bosh-stemcell/spec/support/stemcell_shared_examples.rb +++ b/bosh-stemcell/spec/support/stemcell_shared_examples.rb @@ -34,11 +34,9 @@ it("should be owned by root user (stig: V-38466)") { should be_owned_by("root") } end - # /lib64 holds the x86_64 dynamic linker and is absent on arm64, which uses - # /lib/ld-linux-aarch64.so.1. Skip the ownership check when it is missing, - # mirroring the /usr/lib64 check below. RFC: cloudfoundry/community#1530 - describe command('if [ -e /lib64 ]; then stat -c "%U" /lib64 ; else echo "root" ; fi') do - its(:stdout) { should eq("root\n") } + # /lib64 holds the x86_64 dynamic linker; arm64 uses /lib/ld-linux-aarch64.so.1. + describe file("/lib64"), exclude_on_arm64: true do + it("should be owned by root user (stig: V-38466)") { should be_owned_by("root") } end describe file("/usr/lib") do From 7d1b1e7198432029c082b8860b9a7d6962a7f3f0 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Mon, 2 Nov 2026 09:47:44 -0800 Subject: [PATCH 13/16] refactor(bosh_go_agent): use meta4 from the builder image sim: https://github.com/cloudfoundry/community/pull/1530 --- .../stages/bosh_go_agent/apply.sh | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/stemcell_builder/stages/bosh_go_agent/apply.sh b/stemcell_builder/stages/bosh_go_agent/apply.sh index cbf4b7157a..e5bb3501d7 100755 --- a/stemcell_builder/stages/bosh_go_agent/apply.sh +++ b/stemcell_builder/stages/bosh_go_agent/apply.sh @@ -9,20 +9,11 @@ source $base_dir/lib/prelude_bosh.bash cp -a $assets_dir/alerts.monitrc $chroot/var/vcap/monit/alerts.monitrc cd $assets_dir -# meta4 runs on the build host. dpb587/metalink publishes a linux-amd64 binary -# only, so on other hosts (e.g. arm64 builders) use the meta4 already provided -# by the os-image-stemcell-builder image. RFC: cloudfoundry/community#1530 -if [ "$(uname -m)" == "x86_64" ]; then - wget -O /usr/bin/meta4 https://github.com/dpb587/metalink/releases/download/v0.2.0/meta4-0.2.0-linux-amd64 \ - && echo "81a592eaf647358563f296aced845ac60d9061a45b30b852d1c3f3674720fe19 /usr/bin/meta4" | shasum -a 256 -c \ - && chmod +x /usr/bin/meta4 - meta4_bin=/usr/bin/meta4 -else - meta4_bin="$(command -v meta4 || true)" - if [ -z "${meta4_bin}" ]; then - echo "meta4 not found on this $(uname -m) build host; the os-image-stemcell-builder image provides it" >&2 - exit 1 - fi +# meta4 is provided by the os-image-stemcell-builder image +meta4_bin="$(command -v meta4 || true)" +if [ -z "${meta4_bin}" ]; then + echo "meta4 not found on the build host; the os-image-stemcell-builder image provides it" >&2 + exit 1 fi # Install the agent built for the stemcell's architecture (linux-amd64 or From 43946d58746cd4d307b68aeee7aff28e11fc114b Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Mon, 2 Nov 2026 09:48:55 -0800 Subject: [PATCH 14/16] refactor: add gnu_arch helpers for multiarch triplets sim: https://github.com/cloudfoundry/community/pull/1530 --- bosh-stemcell/lib/bosh/stemcell/arch.rb | 5 +++++ bosh-stemcell/spec/bosh/stemcell/arch_spec.rb | 18 ++++++++++++++++++ bosh-stemcell/spec/os_image/ubuntu_spec.rb | 2 +- stemcell_builder/lib/helpers.sh | 11 +++++++++++ .../stages/password_policies/apply.sh | 6 +----- 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 bosh-stemcell/spec/bosh/stemcell/arch_spec.rb diff --git a/bosh-stemcell/lib/bosh/stemcell/arch.rb b/bosh-stemcell/lib/bosh/stemcell/arch.rb index 4e91c23b21..34d1f3a934 100644 --- a/bosh-stemcell/lib/bosh/stemcell/arch.rb +++ b/bosh-stemcell/lib/bosh/stemcell/arch.rb @@ -7,5 +7,10 @@ def self.arch def self.x86_64? arch == "x86_64" end + + # GNU triplet prefix used for multiarch library paths, e.g. x86_64-linux-gnu + def self.gnu_arch + x86_64? ? "x86_64" : "aarch64" + end end end diff --git a/bosh-stemcell/spec/bosh/stemcell/arch_spec.rb b/bosh-stemcell/spec/bosh/stemcell/arch_spec.rb new file mode 100644 index 0000000000..2acda87175 --- /dev/null +++ b/bosh-stemcell/spec/bosh/stemcell/arch_spec.rb @@ -0,0 +1,18 @@ +require "spec_helper" +require "bosh/stemcell/arch" + +module Bosh::Stemcell + describe Arch do + describe ".gnu_arch" do + it "is x86_64 on an x86_64 host" do + allow(Arch).to receive(:arch).and_return("x86_64") + expect(Arch.gnu_arch).to eq("x86_64") + end + + it "is aarch64 on an aarch64 host" do + allow(Arch).to receive(:arch).and_return("aarch64") + expect(Arch.gnu_arch).to eq("aarch64") + end + end + end +end diff --git a/bosh-stemcell/spec/os_image/ubuntu_spec.rb b/bosh-stemcell/spec/os_image/ubuntu_spec.rb index e45d543b23..2adc5c337d 100644 --- a/bosh-stemcell/spec/os_image/ubuntu_spec.rb +++ b/bosh-stemcell/spec/os_image/ubuntu_spec.rb @@ -168,7 +168,7 @@ end context "PAM configuration" do - pam_multiarch_triplet = Bosh::Stemcell::Arch.x86_64? ? "x86_64-linux-gnu" : "aarch64-linux-gnu" + pam_multiarch_triplet = "#{Bosh::Stemcell::Arch.gnu_arch}-linux-gnu" describe file("/lib/#{pam_multiarch_triplet}/security/pam_pwquality.so") do it { should be_file } end diff --git a/stemcell_builder/lib/helpers.sh b/stemcell_builder/lib/helpers.sh index 80c44f63a8..402cfdf7ac 100644 --- a/stemcell_builder/lib/helpers.sh +++ b/stemcell_builder/lib/helpers.sh @@ -114,3 +114,14 @@ function stemcell_target_arch() { echo "$arch" } + +# GNU triplet prefix used for multiarch library paths (e.g. x86_64-linux-gnu) +function stemcell_gnu_arch() { + local arch + arch="$(stemcell_target_arch "$@")" + case "$arch" in + amd64) echo "x86_64" ;; + arm64) echo "aarch64" ;; + *) echo "Unsupported stemcell architecture: $arch" >&2; return 1 ;; + esac +} diff --git a/stemcell_builder/stages/password_policies/apply.sh b/stemcell_builder/stages/password_policies/apply.sh index d63d717e46..d2f4c426e0 100755 --- a/stemcell_builder/stages/password_policies/apply.sh +++ b/stemcell_builder/stages/password_policies/apply.sh @@ -37,11 +37,7 @@ patch -p1 $chroot/etc/pam.d/common-password < $assets_dir/ubuntu/common-password # (/usr/lib//security/) but PAM's securedir is /usr/lib/security/. # Bridge the gap so PAM can load the module referenced above. The multiarch # triplet depends on the target architecture. -if [ "$(stemcell_target_arch)" == "arm64" ]; then - multiarch_triplet="aarch64-linux-gnu" -else - multiarch_triplet="x86_64-linux-gnu" -fi +multiarch_triplet="$(stemcell_gnu_arch)-linux-gnu" if [ -f "$chroot/usr/lib/${multiarch_triplet}/security/pam_lastlog2.so" ] && \ [ ! -e "$chroot/usr/lib/security/pam_lastlog2.so" ]; then mkdir -p "$chroot/usr/lib/security" From 9e80843e93adda1e64dc4c489e9fae00547fa47e Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Mon, 2 Nov 2026 09:49:17 -0800 Subject: [PATCH 15/16] refactor(static_libraries_config): select static library list per architecture sim: https://github.com/cloudfoundry/community/pull/1530 --- .../stages/static_libraries_config/apply.sh | 16 ++-- .../assets/static_libraries_list_arm64.txt | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 stemcell_builder/stages/static_libraries_config/assets/static_libraries_list_arm64.txt diff --git a/stemcell_builder/stages/static_libraries_config/apply.sh b/stemcell_builder/stages/static_libraries_config/apply.sh index da5aefffbf..1fa2fd2e77 100755 --- a/stemcell_builder/stages/static_libraries_config/apply.sh +++ b/stemcell_builder/stages/static_libraries_config/apply.sh @@ -7,13 +7,17 @@ source $base_dir/lib/prelude_apply.bash source $base_dir/lib/prelude_bosh.bash source $base_dir/etc/settings.bash -cp -p "${assets_dir}/static_libraries_list.txt" $chroot/var/vcap/bosh/etc/static_libraries_list - -# The list is written with the x86_64 multiarch triplet. On arm64 the same -# libraries live under aarch64-linux-gnu. RFC: cloudfoundry/community#1530 -if [ "$(stemcell_target_arch)" == "arm64" ]; then - sed -i 's/x86_64-linux-gnu/aarch64-linux-gnu/g' $chroot/var/vcap/bosh/etc/static_libraries_list +# amd64 keeps the original list file; other architectures use a suffixed list +# because the library paths contain the multiarch triplet. +list_file="${assets_dir}/static_libraries_list.txt" +if [ "$(stemcell_target_arch)" != "amd64" ]; then + list_file="${assets_dir}/static_libraries_list_$(stemcell_target_arch).txt" +fi +if [ ! -f "${list_file}" ]; then + echo "No static libraries list for architecture $(stemcell_target_arch): ${list_file}" >&2 + exit 1 fi +cp -p "${list_file}" $chroot/var/vcap/bosh/etc/static_libraries_list kernel_suffix="-generic" major_kernel_version="6.8" diff --git a/stemcell_builder/stages/static_libraries_config/assets/static_libraries_list_arm64.txt b/stemcell_builder/stages/static_libraries_config/assets/static_libraries_list_arm64.txt new file mode 100644 index 0000000000..73d0fb94d2 --- /dev/null +++ b/stemcell_builder/stages/static_libraries_config/assets/static_libraries_list_arm64.txt @@ -0,0 +1,79 @@ +/usr/lib/gcc/aarch64-linux-gnu/13/libasan.a +/usr/lib/gcc/aarch64-linux-gnu/13/libatomic.a +/usr/lib/gcc/aarch64-linux-gnu/13/libbacktrace.a +/usr/lib/gcc/aarch64-linux-gnu/13/libstdc++_libbacktrace.a +/usr/lib/gcc/aarch64-linux-gnu/13/libgcc.a +/usr/lib/gcc/aarch64-linux-gnu/13/libgcc_eh.a +/usr/lib/gcc/aarch64-linux-gnu/13/libgcov.a +/usr/lib/gcc/aarch64-linux-gnu/13/libgomp.a +/usr/lib/gcc/aarch64-linux-gnu/13/libhwasan.a +/usr/lib/gcc/aarch64-linux-gnu/13/libitm.a +/usr/lib/gcc/aarch64-linux-gnu/13/liblsan.a +/usr/lib/gcc/aarch64-linux-gnu/13/libobjc.a +/usr/lib/gcc/aarch64-linux-gnu/13/libobjc_gc.a +/usr/lib/gcc/aarch64-linux-gnu/13/libquadmath.a +/usr/lib/gcc/aarch64-linux-gnu/13/libssp_nonshared.a +/usr/lib/gcc/aarch64-linux-gnu/13/libstdc++.a +/usr/lib/gcc/aarch64-linux-gnu/13/libstdc++exp.a +/usr/lib/gcc/aarch64-linux-gnu/13/libstdc++fs.a +/usr/lib/gcc/aarch64-linux-gnu/13/libsupc++.a +/usr/lib/gcc/aarch64-linux-gnu/13/libtsan.a +/usr/lib/gcc/aarch64-linux-gnu/13/libubsan.a +/usr/lib/libsupp.a +/usr/lib/aarch64-linux-gnu/gprofng/libgp-collectorAPI.a +/usr/lib/aarch64-linux-gnu/libBrokenLocale.a +/usr/lib/aarch64-linux-gnu/libanl.a +/usr/lib/aarch64-linux-gnu/libbz2.a +/usr/lib/aarch64-linux-gnu/libc.a +/usr/lib/aarch64-linux-gnu/libc_nonshared.a +/usr/lib/aarch64-linux-gnu/libcap.a +/usr/lib/aarch64-linux-gnu/libcrypt.a +/usr/lib/aarch64-linux-gnu/libcrypto.a +/usr/lib/aarch64-linux-gnu/libcurl.a +/usr/lib/aarch64-linux-gnu/libcurses.a +/usr/lib/aarch64-linux-gnu/libdl.a +/usr/lib/aarch64-linux-gnu/libform.a +/usr/lib/aarch64-linux-gnu/libformw.a +/usr/lib/aarch64-linux-gnu/libg.a +/usr/lib/aarch64-linux-gnu/libgcrypt.a +/usr/lib/aarch64-linux-gnu/libgpg-error.a +/usr/lib/aarch64-linux-gnu/libhistory.a +/usr/lib/aarch64-linux-gnu/libicudata.a +/usr/lib/aarch64-linux-gnu/libicui18n.a +/usr/lib/aarch64-linux-gnu/libicuio.a +/usr/lib/aarch64-linux-gnu/libicutest.a +/usr/lib/aarch64-linux-gnu/libicutu.a +/usr/lib/aarch64-linux-gnu/libicuuc.a +/usr/lib/aarch64-linux-gnu/libm-2.35.a +/usr/lib/aarch64-linux-gnu/libm-2.38.a +/usr/lib/aarch64-linux-gnu/libm-2.39.a +/usr/lib/aarch64-linux-gnu/libm.a +/usr/lib/aarch64-linux-gnu/libmcheck.a +/usr/lib/aarch64-linux-gnu/libmenu.a +/usr/lib/aarch64-linux-gnu/libmenuw.a +/usr/lib/aarch64-linux-gnu/libmvec.a +/usr/lib/aarch64-linux-gnu/libncurses++.a +/usr/lib/aarch64-linux-gnu/libncurses++w.a +/usr/lib/aarch64-linux-gnu/libncurses.a +/usr/lib/aarch64-linux-gnu/libncursesw.a +/usr/lib/aarch64-linux-gnu/libnsl.a +/usr/lib/aarch64-linux-gnu/libpanel.a +/usr/lib/aarch64-linux-gnu/libpanelw.a +/usr/lib/aarch64-linux-gnu/libpsx.a +/usr/lib/aarch64-linux-gnu/libpthread.a +/usr/lib/aarch64-linux-gnu/libpthread_nonshared.a +/usr/lib/aarch64-linux-gnu/libreadline.a +/usr/lib/aarch64-linux-gnu/libresolv.a +/usr/lib/aarch64-linux-gnu/librt.a +/usr/lib/aarch64-linux-gnu/libssl.a +/usr/lib/aarch64-linux-gnu/libtermcap.a +/usr/lib/aarch64-linux-gnu/libtic.a +/usr/lib/aarch64-linux-gnu/libtinfo.a +/usr/lib/aarch64-linux-gnu/libtirpc.a +/usr/lib/aarch64-linux-gnu/libutil.a +/usr/lib/aarch64-linux-gnu/libuuid.a +/usr/lib/aarch64-linux-gnu/libxml2.a +/usr/lib/aarch64-linux-gnu/libz.a +/usr/src/linux-headers-__KERNEL_VERSION__/tools/bpf/resolve_btfids/libbpf/libbpf.a +/usr/src/linux-headers-__KERNEL_VERSION__/tools/bpf/resolve_btfids/libsubcmd/libsubcmd.a +/usr/src/linux-headers-__KERNEL_VERSION__/tools/objtool/libsubcmd/libsubcmd.a From 64d6836e1174c845d434665881b312066d2dbb74 Mon Sep 17 00:00:00 2001 From: Sachin Vighe Date: Mon, 2 Nov 2026 10:29:33 -0800 Subject: [PATCH 16/16] test(stemcell): add arm64 package list for the Ubuntu base packages check sim: https://github.com/cloudfoundry/community/pull/1530 --- .../spec/assets/dpkg-list-ubuntu-arm64.txt | 495 ++++++++++++++++++ bosh-stemcell/spec/stemcells/ubuntu_spec.rb | 4 +- 2 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 bosh-stemcell/spec/assets/dpkg-list-ubuntu-arm64.txt diff --git a/bosh-stemcell/spec/assets/dpkg-list-ubuntu-arm64.txt b/bosh-stemcell/spec/assets/dpkg-list-ubuntu-arm64.txt new file mode 100644 index 0000000000..121c43f288 --- /dev/null +++ b/bosh-stemcell/spec/assets/dpkg-list-ubuntu-arm64.txt @@ -0,0 +1,495 @@ +adduser +anacron +apparmor +apparmor-utils +apt +apt-utils +auditd +autoconf +automake +autopoint +autotools-dev +base-files +base-passwd +bash +bind9-dnsutils +bind9-host +bind9-libs:arm64 +binutils +binutils-aarch64-linux-gnu +binutils-common:arm64 +bison +bsdextrautils +bsdutils +build-essential +busybox-initramfs +bzip2 +ca-certificates +chrony +cloud-guest-utils +cmake +cmake-data +console-setup +console-setup-linux +coreutils +cpio +cpp +cpp-13 +cpp-13-aarch64-linux-gnu +cpp-aarch64-linux-gnu +cron +cron-daemon-common +curl +dash +dbus +dbus-bin +dbus-daemon +dbus-session-bus-common +dbus-system-bus-common +dbus-user-session +debconf +debconf-i18n +debhelper +debianutils +debugedit +dh-autoreconf +dh-strip-nondeterminism +dhcpcd-base +diffutils +distro-info +distro-info-data +dmsetup +dnsutils +dpkg +dpkg-dev +dracut-install +dwz +e2fsprogs +eject +ethtool +fdisk +file +findutils +flex +g++ +g++-13 +g++-13-aarch64-linux-gnu +g++-aarch64-linux-gnu +gcc +gcc-13 +gcc-13-aarch64-linux-gnu +gcc-13-base:arm64 +gcc-14-base:arm64 +gcc-aarch64-linux-gnu +gdb +gdisk +gettext +gettext-base +gir1.2-girepository-2.0:arm64 +gir1.2-glib-2.0:arm64 +gir1.2-packagekitglib-1.0 +gpg +gpg-agent +gpgconf +gpgv +grep +groff-base +grub-common +grub-efi-arm64 +grub-efi-arm64-bin +grub-efi-arm64-signed +grub2-common +gzip +hostname +htop +icu-devtools +ifupdown +init +init-system-helpers +initramfs-tools +initramfs-tools-bin +initramfs-tools-core +intltool-debian +iproute2 +iptables +iputils-arping +iputils-ping +iso-codes +kbd +keyboard-configuration +klibc-utils +kmod +krb5-locales +less +libacl1:arm64 +libaio1t64:arm64 +libapparmor1:arm64 +libappstream5:arm64 +libapt-pkg6.0t64:arm64 +libarchive-zip-perl +libarchive13t64:arm64 +libargon2-1:arm64 +libasan8:arm64 +libassuan0:arm64 +libatm1t64:arm64 +libatomic1:arm64 +libattr1:arm64 +libaudit-common +libaudit1:arm64 +libauparse0t64:arm64 +libbabeltrace1:arm64 +libbinutils:arm64 +libblkid1:arm64 +libbpf1:arm64 +libbrotli1:arm64 +libbsd0:arm64 +libbz2-1.0:arm64 +libbz2-dev:arm64 +libc-bin +libc-dev-bin +libc6-dev:arm64 +libc6:arm64 +libcap-dev:arm64 +libcap-ng0:arm64 +libcap2-bin +libcap2:arm64 +libcbor0.10:arm64 +libcc1-0:arm64 +libcom-err2:arm64 +libcrack2:arm64 +libcrypt-dev:arm64 +libcrypt1:arm64 +libcryptsetup12:arm64 +libctf-nobfd0:arm64 +libctf0:arm64 +libcurl3t64-gnutls:arm64 +libcurl4-openssl-dev:arm64 +libcurl4t64:arm64 +libdb5.3t64:arm64 +libdbus-1-3:arm64 +libdebconfclient0:arm64 +libdebhelper-perl +libdebuginfod-common +libdebuginfod1t64:arm64 +libdevmapper1.02.1:arm64 +libdpkg-perl +libduktape207:arm64 +libdw1t64:arm64 +libedit2:arm64 +libefiboot1t64:arm64 +libefivar1t64:arm64 +libelf1t64:arm64 +libestr0:arm64 +libexpat1:arm64 +libext2fs2t64:arm64 +libfastjson4:arm64 +libfdisk1:arm64 +libffi8:arm64 +libfido2-1:arm64 +libfile-stripnondeterminism-perl +libfreetype6:arm64 +libfribidi0:arm64 +libfuse3-3:arm64 +libgcc-13-dev:arm64 +libgcc-s1:arm64 +libgcrypt20-dev +libgcrypt20:arm64 +libgdbm-compat4t64:arm64 +libgdbm6t64:arm64 +libgirepository-1.0-1:arm64 +libglib2.0-0t64:arm64 +libglib2.0-bin +libglib2.0-data +libgmp10:arm64 +libgnutls30t64:arm64 +libgomp1:arm64 +libgpg-error-dev +libgpg-error0:arm64 +libgprofng0:arm64 +libgssapi-krb5-2:arm64 +libgstreamer1.0-0:arm64 +libhogweed6t64:arm64 +libhwasan0:arm64 +libibverbs1:arm64 +libicu-dev:arm64 +libicu74:arm64 +libidn2-0:arm64 +libinih1:arm64 +libip4tc2:arm64 +libip6tc2:arm64 +libisl23:arm64 +libitm1:arm64 +libjansson4:arm64 +libjson-c5:arm64 +libjsoncpp25:arm64 +libk5crypto3:arm64 +libkeyutils1:arm64 +libklibc:arm64 +libkmod2:arm64 +libkrb5-3:arm64 +libkrb5support0:arm64 +libldap2:arm64 +liblmdb0:arm64 +liblocale-gettext-perl +liblsan0:arm64 +liblz4-1:arm64 +liblzma5:arm64 +libmagic-mgc +libmagic1t64:arm64 +libmaxminddb0:arm64 +libmd0:arm64 +libmnl0:arm64 +libmount1:arm64 +libmpc3:arm64 +libmpfr6:arm64 +libncurses-dev:arm64 +libncurses6:arm64 +libncursesw6:arm64 +libnetfilter-conntrack3:arm64 +libnetplan1:arm64 +libnettle8t64:arm64 +libnewt0.52:arm64 +libnfnetlink0:arm64 +libnftables1:arm64 +libnftnl11:arm64 +libnghttp2-14:arm64 +libnl-3-200:arm64 +libnl-genl-3-200:arm64 +libnl-route-3-200:arm64 +libnpth0t64:arm64 +libnss-systemd:arm64 +libnvme1t64 +libp11-kit0:arm64 +libpackagekit-glib2-18:arm64 +libpam-cap:arm64 +libpam-modules-bin +libpam-modules:arm64 +libpam-pwquality:arm64 +libpam-runtime +libpam-systemd:arm64 +libpam0g:arm64 +libparted2t64:arm64 +libpcap0.8t64:arm64 +libpcre2-8-0:arm64 +libperl5.38t64:arm64 +libpipeline1:arm64 +libpng16-16t64:arm64 +libpolkit-agent-1-0:arm64 +libpolkit-gobject-1-0:arm64 +libpopt0:arm64 +libproc2-0:arm64 +libpsl5t64:arm64 +libpwquality-common +libpwquality1:arm64 +libpython3-stdlib:arm64 +libpython3.12-minimal:arm64 +libpython3.12-stdlib:arm64 +libpython3.12t64:arm64 +libreadline-dev:arm64 +libreadline8t64:arm64 +librelp0:arm64 +librhash0:arm64 +librtmp1:arm64 +libsasl2-2:arm64 +libsasl2-modules-db:arm64 +libseccomp2:arm64 +libselinux1:arm64 +libsemanage-common +libsemanage2:arm64 +libsensors-config +libsensors5:arm64 +libsepol2:arm64 +libsframe1:arm64 +libslang2:arm64 +libsmartcols1:arm64 +libsource-highlight-common +libsource-highlight4t64:arm64 +libsqlite3-0:arm64 +libss2:arm64 +libssh-4:arm64 +libssl-dev:arm64 +libssl3t64:arm64 +libstdc++-13-dev:arm64 +libstdc++6:arm64 +libstemmer0d:arm64 +libsub-override-perl +libsystemd-shared:arm64 +libsystemd0:arm64 +libtasn1-6:arm64 +libtext-charwidth-perl:arm64 +libtext-iconv-perl:arm64 +libtext-wrapi18n-perl +libtinfo6:arm64 +libtirpc-common +libtirpc3t64:arm64 +libtool +libtsan2:arm64 +libubsan1:arm64 +libuchardet0:arm64 +libudev1:arm64 +libunistring5:arm64 +libunwind8:arm64 +liburcu8t64:arm64 +libuuid1:arm64 +libuv1t64:arm64 +libwrap0:arm64 +libxml2-dev:arm64 +libxml2:arm64 +libxmlb2:arm64 +libxslt1-dev:arm64 +libxslt1.1:arm64 +libxtables12:arm64 +libxxhash0:arm64 +libyaml-0-2:arm64 +libzstd1:arm64 +linux-base +linux-firmware +linux-firmware-amd-graphics +linux-firmware-amd-misc +linux-firmware-broadcom-wireless +linux-firmware-intel-graphics +linux-firmware-intel-misc +linux-firmware-intel-wireless +linux-firmware-marvell-prestera +linux-firmware-marvell-wireless +linux-firmware-mediatek +linux-firmware-mellanox-spectrum +linux-firmware-misc +linux-firmware-netronome +linux-firmware-nvidia-graphics +linux-firmware-qlogic +linux-firmware-qualcomm-graphics +linux-firmware-qualcomm-misc +linux-firmware-qualcomm-wireless +linux-firmware-realtek +linux-libc-dev:arm64 +locales +login +logrotate +logsave +lsb-release +lsof +lto-disabled-list +m4 +make +man-db +mawk +media-types +module-assistant +mokutil +mount +ncurses-base +ncurses-bin +net-tools +netbase +netcat-openbsd +netplan-generator +networkd-dispatcher +nftables +nvme-cli +openssh-client +openssh-server +openssh-sftp-server +openssl +packagekit +parted +passwd +patch +perl +perl-base +perl-modules-5.38 +pinentry-curses +po-debconf +polkitd +procps +psmisc +python-apt-common +python3 +python3-apparmor +python3-apt +python3-blinker +python3-cffi-backend:arm64 +python3-cryptography +python3-dbus +python3-distro +python3-gi +python3-httplib2 +python3-jwt +python3-launchpadlib +python3-lazr.restfulclient +python3-lazr.uri +python3-libapparmor +python3-markdown-it +python3-mdurl +python3-minimal +python3-netifaces:arm64 +python3-netplan +python3-oauthlib +python3-pkg-resources +python3-pygments +python3-pyparsing +python3-rich +python3-six +python3-software-properties +python3-typing-extensions +python3-wadllib +python3-yaml +python3.12 +python3.12-minimal +quota +readline-common +rng-tools-debian +rpcsvc-proto +rsync +rsyslog +rsyslog-gnutls +rsyslog-openssl +rsyslog-relp +runit +sbsigntool +sed +sensible-utils +sgml-base +shared-mime-info +shim-signed +software-properties-common +strace +sudo +sysstat +systemd +systemd-dev +systemd-hwe-hwdb +systemd-resolved +systemd-sysv +systemd-timesyncd +sysuser-helper +sysvinit-utils +tar +tcpdump +traceroute +tzdata +tzdata-legacy +ubuntu-keyring +ubuntu-pro-client +ubuntu-pro-client-l10n +ucf +udev +unzip +util-linux +uuid-dev:arm64 +uuid-runtime +vim-common +vim-runtime +vim-tiny +wget +whiptail +wireless-regdb +xdg-user-dirs +xfsprogs +xkb-data +xml-core +xxd +xz-utils +zip +zlib1g-dev:arm64 +zlib1g:arm64 diff --git a/bosh-stemcell/spec/stemcells/ubuntu_spec.rb b/bosh-stemcell/spec/stemcells/ubuntu_spec.rb index f1e879685d..84ce2f0d67 100644 --- a/bosh-stemcell/spec/stemcells/ubuntu_spec.rb +++ b/bosh-stemcell/spec/stemcells/ubuntu_spec.rb @@ -452,7 +452,9 @@ dpkg_list_packages = "dpkg --get-selections | cut -f1 | sed -E '#{linux_version_regex}'" # TODO: maby we can use awk "dpkg --get-selections | awk '!/linux-(.+)-([0-9]+.+)/&&/linux/{print $1}'" - let(:dpkg_list_ubuntu) { File.readlines(spec_asset("dpkg-list-ubuntu.txt")).map(&:chop) } + # Package names carry the architecture (e.g. libc6:amd64), so arm64 has its own list. + dpkg_list_ubuntu_file = Bosh::Stemcell::Arch.x86_64? ? "dpkg-list-ubuntu.txt" : "dpkg-list-ubuntu-arm64.txt" + let(:dpkg_list_ubuntu) { File.readlines(spec_asset(dpkg_list_ubuntu_file)).map(&:chop) } let(:dpkg_list_kernel_ubuntu) { File.readlines(spec_asset("dpkg-list-ubuntu-kernel.txt")).map(&:chop) } let(:dpkg_list_google_ubuntu) { File.readlines(spec_asset("dpkg-list-ubuntu-google-additions.txt")).map(&:chop) } let(:dpkg_list_vsphere_ubuntu) { File.readlines(spec_asset("dpkg-list-ubuntu-vsphere-additions.txt")).map(&:chop) }