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/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/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..26e972166c 100644 --- a/bosh-stemcell/lib/bosh/stemcell/definition.rb +++ b/bosh-stemcell/lib/bosh/stemcell/definition.rb @@ -3,20 +3,24 @@ module Bosh::Stemcell class Definition - attr_reader :infrastructure, :hypervisor_name, :operating_system + 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 +32,8 @@ 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 + # amd64 stemcell names carry no architecture suffix; other architectures do. + stemcell_name_parts << architecture unless architecture == DEFAULT_ARCHITECTURE stemcell_name_parts.join("-") end @@ -38,7 +44,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/infrastructure.rb b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb index be37b1a58e..72d0234425 100644 --- a/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb +++ b/bosh-stemcell/lib/bosh/stemcell/infrastructure.rb @@ -46,6 +46,15 @@ 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; 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}" + end + end + def ==(other) name == other.name && hypervisor == other.hypervisor && @@ -129,6 +138,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 2e339caf77..298a59672e 100644 --- a/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb +++ b/bosh-stemcell/lib/bosh/stemcell/stemcell_packager.rb @@ -49,7 +49,7 @@ def manifest(disk_format) end def manifest_cloud_properties(disk_format, infrastructure, stemcell_name) - architecture = "x86_64" + architecture = infrastructure.architecture_name(definition.architecture) { "name" => stemcell_name, 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/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/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 9476dcedf6..05d7d0d049 100644 --- a/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb +++ b/bosh-stemcell/spec/bosh/stemcell/stemcell_packager_spec.rb @@ -124,6 +124,40 @@ 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 + + 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 + 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") diff --git a/bosh-stemcell/spec/os_image/ubuntu_spec.rb b/bosh-stemcell/spec/os_image/ubuntu_spec.rb index 7e6f1faf9d..e45d543b23 100644 --- a/bosh-stemcell/spec/os_image/ubuntu_spec.rb +++ b/bosh-stemcell/spec/os_image/ubuntu_spec.rb @@ -33,9 +33,10 @@ 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' } + # 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 describe file("/lib/systemd/system/monit.service") do @@ -86,14 +87,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 @@ -163,7 +168,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/bosh-stemcell/spec/support/stemcell_shared_examples.rb b/bosh-stemcell/spec/support/stemcell_shared_examples.rb index 4c5b72bbfe..f37c2e7dc2 100644 --- a/bosh-stemcell/spec/support/stemcell_shared_examples.rb +++ b/bosh-stemcell/spec/support/stemcell_shared_examples.rb @@ -34,7 +34,8 @@ it("should be owned by root user (stig: V-38466)") { should be_owned_by("root") } end - describe file("/lib64") do + # /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 diff --git a/ci/docker/os-image-stemcell-builder/Dockerfile b/ci/docker/os-image-stemcell-builder/Dockerfile index 40fc8e6f1a..4d2afc7628 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,15 @@ 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 target, so it is only installed on amd64. +# +# 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,9 +92,32 @@ 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 META4_VERSION +ARG SYFT_CLI_URL +ARG YQ_CLI_URL +ARG RUBY_INSTALL_URL +ARG RUBY_VERSION +ARG GEM_HOME + +# 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" \ - && 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 74511f70ce..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 @) @@ -187,6 +188,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 +251,50 @@ jobs: params: image: image/image.tar +- 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 @@ -428,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: @@ -447,6 +494,53 @@ jobs: params: file: os-image-version/number +- 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-arm64 + tags: [ arm64 ] + - get: bosh-linux-stemcell-builder + trigger: true + tags: [ arm64 ] + - get: os-image-version-arm64 + params: + bump: "major" + tags: [ arm64 ] + - get: usn-log + - get: stemcell-trigger + trigger: true + - task: build + file: bosh-stemcells-ci/ci/tasks/os-images/build.yml + 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: + image_os_tag: (@= data.values.stemcell_details.os_short_name @) + - put: os-image-tarball-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 @@ -638,6 +732,66 @@ jobs: - #@ build_stemcell(iaas.iaas, iaas.hypervisor, "-fips") #@ end +- 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 + - 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 + 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: @@ -1645,6 +1799,24 @@ resources: - #@ metalink_resource(iaas.iaas, iaas.hypervisor, "-fips") #@ end +- 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: @@ -1663,6 +1835,24 @@ resources: secret_key: ((hmac_secret)) type: s3 +- 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: @@ -1681,6 +1871,15 @@ resources: file: (@= data.values.stemcell_details.branch @)/os-image-version private_key: ((github_deploy_key_bosh-linux-stemcell-builder.private_key)) +- 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: @@ -1872,6 +2071,14 @@ resources: username: ((dockerhub_username)) password: ((dockerhub_password)) +- 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..b1e90f3a91 100755 --- a/ci/tasks/build-docker-args.sh +++ b/ci/tasks/build-docker-args.sh @@ -18,12 +18,23 @@ 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 workers. +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 +44,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}" diff --git a/ci/tasks/build.sh b/ci/tasks/build.sh index 7046eeedf5..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)" @@ -86,22 +87,32 @@ 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 # # 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_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" 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 @@ -109,8 +120,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}" @@ -130,4 +141,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/ci/tasks/build.yml b/ci/tasks/build.yml index beae4ce7f0..d8b8eb08c6 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: 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 5b55f27f79..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 @@ -44,5 +45,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..61ae469cd3 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: replace-me ESM_TOKEN: UBUNTU_ADVANTAGE_TOKEN: UBUNTU_DEBOOTSTRAP_MIRROR: diff --git a/stemcell_builder/lib/helpers.sh b/stemcell_builder/lib/helpers.sh index 2ca4bbdaaa..80c44f63a8 100644 --- a/stemcell_builder/lib/helpers.sh +++ b/stemcell_builder/lib/helpers.sh @@ -94,3 +94,23 @@ 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 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 +# 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..29b84d111c 100755 --- a/stemcell_builder/stages/base_apt/apply.sh +++ b/stemcell_builder/stages/base_apt/apply.sh @@ -8,6 +8,16 @@ 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. +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" <.url/.sha256sum assets point at linux-amd64 builds. Other +# architectures (e.g. arm64) read .linux-.url/.sha256sum. +# 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/image_install_grub/apply.sh b/stemcell_builder/stages/image_install_grub/apply.sh index ce64080679..aec7869286 100755 --- a/stemcell_builder/stages/image_install_grub/apply.sh +++ b/stemcell_builder/stages/image_install_grub/apply.sh @@ -76,8 +76,13 @@ echo "(hd0) ${device}" > ${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/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" diff --git a/stemcell_builder/stages/system_grub/apply.sh b/stemcell_builder/stages/system_grub/apply.sh index 60798c3b0c..82f2b2748f 100755 --- a/stemcell_builder/stages/system_grub/apply.sh +++ b/stemcell_builder/stages/system_grub/apply.sh @@ -5,7 +5,13 @@ 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. +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.