diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index 62feb62c71e..62eaf571741 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -878,10 +878,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-amd64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209152.2' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16ds_v5' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -904,10 +903,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260706.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-amd64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209152.2' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D4ads_v5' echo '##vso[task.setvariable variable=CVM_TEST_VM_SIZE]Standard_DC8es_v6' @@ -930,10 +928,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-arm64-gen2-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-arm64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209110.1' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16pds_v6' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -956,10 +953,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-amd64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209152.2' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16ds_v5' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -982,10 +978,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-arm64-gen2-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-arm64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209110.1' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16pds_v6' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' diff --git a/.pipelines/.vsts-vhd-builder.yaml b/.pipelines/.vsts-vhd-builder.yaml index a7e955095e3..5950c21b38e 100644 --- a/.pipelines/.vsts-vhd-builder.yaml +++ b/.pipelines/.vsts-vhd-builder.yaml @@ -245,10 +245,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-amd64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209152.2' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16ds_v5' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -268,10 +267,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-arm64-gen2-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-arm64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209110.1' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16pds_v6' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -291,10 +289,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-amd64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209152.2' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16ds_v5' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' @@ -314,10 +311,9 @@ stages: - bash: | echo '##vso[task.setvariable variable=OS_SKU]AzureContainerLinux' echo '##vso[task.setvariable variable=OS_VERSION]acl' - echo '##vso[task.setvariable variable=IMG_PUBLISHER]MicrosoftCBLMariner' - echo '##vso[task.setvariable variable=IMG_OFFER]azure-linux-3' - echo '##vso[task.setvariable variable=IMG_SKU]azure-linux-3-arm64-gen2-acl' - echo '##vso[task.setvariable variable=IMG_VERSION]3.20260809.01' + echo '##vso[task.setvariable variable=SIG_SOURCE_GALLERY_UNIQUE_NAME]ae6ab47f-1fc9-4b67-8f82-4636119119c9-ACL' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_NAME]acl-3.0-arm64' + echo '##vso[task.setvariable variable=SIG_SOURCE_IMAGE_VERSION]20260923.1209110.1' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_D16pds_v6' echo '##vso[task.setvariable variable=FEATURE_FLAGS]None' diff --git a/spec/vhdbuilder/packer/ensure_sig_image_name_linux_spec.sh b/spec/vhdbuilder/packer/ensure_sig_image_name_linux_spec.sh index b36b4d0f525..0e15ba57dae 100644 --- a/spec/vhdbuilder/packer/ensure_sig_image_name_linux_spec.sh +++ b/spec/vhdbuilder/packer/ensure_sig_image_name_linux_spec.sh @@ -212,13 +212,13 @@ Describe 'ensure_sig_image_name_linux function' End End - Describe 'IMG_OFFER azure-linux-3 with OS_SKU AzureContainerLinux (ACL) scenarios' - # ACL shares the azure-linux-3 marketplace offer but its destination SIG image + Describe 'OS_SKU AzureContainerLinux (ACL) scenarios' + # ACL uses a direct shared gallery source image. Its destination SIG image # definitions (aclgen2TL, aclgen2arm64TL) intentionally have no AzureLinux prefix. It 'should NOT add AzureLinux prefix when OS_SKU is AzureContainerLinux (gen2 TL)' SIG_IMAGE_NAME="" SKU_NAME="aclgen2TL" - IMG_OFFER="azure-linux-3" + IMG_OFFER="" OS_SKU="AzureContainerLinux" When call ensure_sig_image_name_linux The status should be success @@ -229,7 +229,7 @@ Describe 'ensure_sig_image_name_linux function' It 'should NOT add AzureLinux prefix when OS_SKU is AzureContainerLinux (arm64 gen2 TL)' SIG_IMAGE_NAME="" SKU_NAME="aclgen2arm64TL" - IMG_OFFER="azure-linux-3" + IMG_OFFER="" OS_SKU="AzureContainerLinux" When call ensure_sig_image_name_linux The status should be success @@ -240,7 +240,7 @@ Describe 'ensure_sig_image_name_linux function' It 'should handle case-insensitive OS_SKU azurecontainerlinux (lowercase)' SIG_IMAGE_NAME="" SKU_NAME="aclgen2TL" - IMG_OFFER="azure-linux-3" + IMG_OFFER="" OS_SKU="azurecontainerlinux" When call ensure_sig_image_name_linux The status should be success @@ -251,7 +251,7 @@ Describe 'ensure_sig_image_name_linux function' It 'should handle case-insensitive OS_SKU AzureContainerLinux (uppercase)' SIG_IMAGE_NAME="" SKU_NAME="aclgen2TL" - IMG_OFFER="azure-linux-3" + IMG_OFFER="" OS_SKU="AZURECONTAINERLINUX" When call ensure_sig_image_name_linux The status should be success diff --git a/spec/vhdbuilder/packer/generate_vhd_publishing_info_spec.sh b/spec/vhdbuilder/packer/generate_vhd_publishing_info_spec.sh new file mode 100644 index 00000000000..8edecbcf47b --- /dev/null +++ b/spec/vhdbuilder/packer/generate_vhd_publishing_info_spec.sh @@ -0,0 +1,243 @@ +#!/bin/bash +# shellcheck disable=SC2329 + +Describe 'generate-vhd-publishing-info.sh' + setup() { + SCRIPT_PATH="${PWD}/vhdbuilder/packer/generate-vhd-publishing-info.sh" + TEST_DIR=$(mktemp -d "${SHELLSPEC_WORKDIR}/publishing-info.XXXXXX") + OUTPUT="${TEST_DIR}/vhd-publishing-info.json" + export AZ_CALLS="${TEST_DIR}/az-calls" + : > "$AZ_CALLS" + export AZ_STATUS=0 + export AZ_VERSIONS=$'2026.09.02\n2026.09.01\n2026.09.02' + export STORAGE_ACCT_BLOB_URL=https://example.invalid/images VHD_NAME=test.vhd + export OS_NAME=linux OFFER_NAME=Ubuntu SKU_NAME=2204 HYPERV_GENERATION=V2 + export IMAGE_VERSION=202610.01.0 SECURITY_TYPE_FEATURE=TrustedLaunch ARCHITECTURE=amd64 + export SUBSCRIPTION_ID=test-subscription RESOURCE_GROUP_NAME=test-rg + export SIG_IMAGE_NAME=captured-image CAPTURED_SIG_VERSION=202610.01.1 + export IMG_PUBLISHER=Canonical IMG_OFFER=ubuntu-offer IMG_SKU=ubuntu-sku + export IMG_VERSION=2026.09.01 + unset SIG_SOURCE_GALLERY_UNIQUE_NAME SIG_SOURCE_IMAGE_NAME SIG_SOURCE_IMAGE_VERSION + unset PUBLISHER_BASE_IMAGE_VERSION PUBLISHER_BASE_IMAGE_SKU + unset BUILDER BASE_IMG BASE_IMG_VERSION + } + BeforeEach 'setup' + + az() { + printf '<%s>\n' "$@" >> "$AZ_CALLS" + if [ "$AZ_STATUS" -ne 0 ]; then + echo 'mock Azure CLI lookup failed' >&2 + fi + printf '%s\n' "$AZ_VERSIONS" + return "$AZ_STATUS" + } + + generate_publishing_info() ( + cd "$TEST_DIR" || return + export -f az + bash -e "$SCRIPT_PATH" 2> "${TEST_DIR}/stderr" + ) + + read_metadata() { + jq -cr "$1" "$OUTPUT" + } + + set_acg_source() { + export SIG_SOURCE_GALLERY_UNIQUE_NAME=shared-acl-gallery + export SIG_SOURCE_IMAGE_NAME=acl-3.0-amd64 SIG_SOURCE_IMAGE_VERSION=20260923.1209152.2 + OFFER_NAME=AzureContainerLinux + SKU_NAME=acl-tl + } + + Describe 'ACG sources' + Parameters + amd64 acl-3.0-amd64 20260923.1209152.2 acl-tl x64 + amd64 acl-3.0-amd64 20260923.1209152.2 acl-cvm x64 + arm64 acl-3.0-arm64 20260923.1209110.1 acl-arm64-tl Arm64 + End + + It "records the pinned $2 source for $4 without a Marketplace lookup" + set_acg_source + ARCHITECTURE="$1" SIG_SOURCE_IMAGE_NAME="$2" SIG_SOURCE_IMAGE_VERSION="$3" SKU_NAME="$4" + unset IMG_PUBLISHER IMG_OFFER IMG_SKU + + When call generate_publishing_info + The status should be success + The output should include "ACG base image shared-acl-gallery/$2/$3" + The contents of file "$AZ_CALLS" should equal '' + The contents of file "${TEST_DIR}/stderr" should not include 'WARNING:' + The value "$(read_metadata '.publisher_base_image_version')" should equal "$3" + The value "$(read_metadata '.publisher_base_image_sku')" should equal "$2" + The value "$(read_metadata '.image_architecture')" should equal "$5" + The value "$(read_metadata '.sku_name')" should equal "$4" + The value "$(read_metadata '.replication_inverse')" should equal 4 + The value "$(read_metadata '.vhd_url')" should equal 'https://example.invalid/images/test.vhd' + The value "$(read_metadata '.captured_sig_resource_id')" should equal '/subscriptions/test-subscription/resourceGroups/test-rg/providers/Microsoft.Compute/galleries/PackerSigGalleryEastUS/images/captured-image/versions/202610.01.1' + The value "$(read_metadata '[.os_name, .offer_name, .hyperv_generation, .image_version, .security_type_feature]')" should equal '["linux","AzureContainerLinux","V2","202610.01.0","TrustedLaunch"]' + The value "$(read_metadata 'keys | length')" should equal 12 + End + End + + It 'prefers ACG source metadata over stale Marketplace values' + set_acg_source + + When call generate_publishing_info + The status should be success + The output should include 'ACG base image shared-acl-gallery/acl-3.0-amd64/20260923.1209152.2' + The contents of file "$AZ_CALLS" should equal '' + The value "$(read_metadata '.publisher_base_image_version')" should equal '20260923.1209152.2' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'acl-3.0-amd64' + End + + Describe 'incomplete ACG configuration' + Parameters + '' acl-3.0-amd64 20260923.1209152.2 SIG_SOURCE_GALLERY_UNIQUE_NAME + shared-acl-gallery '' 20260923.1209152.2 SIG_SOURCE_IMAGE_NAME + shared-acl-gallery acl-3.0-amd64 '' SIG_SOURCE_IMAGE_VERSION + shared-acl-gallery '' '' SIG_SOURCE_IMAGE_NAME + '' acl-3.0-amd64 '' SIG_SOURCE_GALLERY_UNIQUE_NAME + '' '' 20260923.1209152.2 SIG_SOURCE_GALLERY_UNIQUE_NAME + End + + It "warns about missing $4 without blocking publication or using Marketplace metadata" + OFFER_NAME=AzureContainerLinux + export SIG_SOURCE_GALLERY_UNIQUE_NAME="$1" SIG_SOURCE_IMAGE_NAME="$2" SIG_SOURCE_IMAGE_VERSION="$3" + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "${TEST_DIR}/stderr" should include "WARNING: $4 is not set for ACG source metadata; continuing" + The contents of file "$AZ_CALLS" should equal '' + The value "$(read_metadata '.publisher_base_image_version')" should equal "$3" + The value "$(read_metadata '.publisher_base_image_sku')" should equal "$2" + The value "$(read_metadata 'keys | length')" should equal 12 + End + End + + It 'warns and still publishes when all ACL source metadata is absent' + OFFER_NAME=AzureContainerLinux + unset IMG_PUBLISHER IMG_OFFER IMG_SKU + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "$AZ_CALLS" should equal '' + The contents of file "${TEST_DIR}/stderr" should include 'WARNING: SIG_SOURCE_GALLERY_UNIQUE_NAME is not set' + The contents of file "${TEST_DIR}/stderr" should include 'WARNING: SIG_SOURCE_IMAGE_NAME is not set' + The contents of file "${TEST_DIR}/stderr" should include 'WARNING: SIG_SOURCE_IMAGE_VERSION is not set' + The value "$(read_metadata '.publisher_base_image_version')" should equal '' + The value "$(read_metadata '.publisher_base_image_sku')" should equal '' + The value "$(read_metadata 'keys | length')" should equal 12 + End + + Describe 'non-ACL source selection' + Parameters + Ubuntu + AzureLinux + AzureLinuxOSGuard + End + + It "does not select ACG metadata for $1 because of a leftover gallery variable" + OFFER_NAME="$1" + export SIG_SOURCE_GALLERY_UNIQUE_NAME=stale-gallery + + When call generate_publishing_info + The status should be success + The output should include 'Latest Canonical base image version' + The contents of file "$AZ_CALLS" should include '' + The contents of file "${TEST_DIR}/stderr" should not include 'WARNING:' + The value "$(read_metadata '.publisher_base_image_version')" should equal '2026.09.02' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'ubuntu-sku' + End + End + + It 'preserves Marketplace version selection and Linux metadata' + When call generate_publishing_info + The status should be success + The output should include 'Latest Canonical base image version for offer ubuntu-offer and sku ubuntu-sku is 2026.09.02' + The value "$(read_metadata '.publisher_base_image_version')" should equal '2026.09.02' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'ubuntu-sku' + The value "$(read_metadata '[.os_name, .offer_name, .sku_name, .hyperv_generation, .image_architecture, .image_version, .security_type_feature, .replication_inverse]')" should equal '["linux","Ubuntu","2204","V2","x64","202610.01.0","TrustedLaunch","1"]' + The value "$(read_metadata 'keys | length')" should equal 12 + The contents of file "$AZ_CALLS" should equal "$(printf '<%s>\n' vm image list -p Canonical -s ubuntu-sku --query "[?offer=='ubuntu-offer'].version" -o tsv --all)" + End + + It 'preserves OSGuard publishing when its legacy Marketplace lookup fails' + export BUILDER=imagecustomizer BASE_IMG=mcr.microsoft.com/azurelinux/3.0/image/osguard BASE_IMG_VERSION=3.0.20260107 + OFFER_NAME=AzureLinuxOSGuard SKU_NAME=OSGuardV3gen2fipsTL IMG_SKU=azure-linux-osguard-3 + unset IMG_PUBLISHER IMG_OFFER + AZ_STATUS=2 AZ_VERSIONS='' + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "${TEST_DIR}/stderr" should include 'mock Azure CLI lookup failed' + The contents of file "$AZ_CALLS" should include '' + The value "$(read_metadata '.publisher_base_image_version')" should equal '' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'azure-linux-osguard-3' + The value "$(read_metadata 'keys | length')" should equal 12 + End + + Describe 'missing Marketplace inputs' + Parameters + IMG_PUBLISHER + IMG_OFFER + IMG_SKU + End + + It "does not introduce a new failure for missing $1" + unset "$1" + AZ_STATUS=2 AZ_VERSIONS='' + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "${TEST_DIR}/stderr" should include 'mock Azure CLI lookup failed' + The contents of file "$AZ_CALLS" should include '' + The value "$(read_metadata '.publisher_base_image_version')" should equal '' + The value "$(read_metadata '.publisher_base_image_sku')" should equal "${IMG_SKU:-}" + End + End + + It 'preserves legacy version selection when the CLI fails with partial output' + AZ_STATUS=2 + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "${TEST_DIR}/stderr" should include 'mock Azure CLI lookup failed' + The value "$(read_metadata '.publisher_base_image_version')" should equal '2026.09.02' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'ubuntu-sku' + End + + It 'preserves publishing when the Marketplace lookup finds no versions' + AZ_VERSIONS='' + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The value "$(read_metadata '.publisher_base_image_version')" should equal '' + The value "$(read_metadata '.publisher_base_image_sku')" should equal 'ubuntu-sku' + End + + Describe 'Windows output' + Parameters + '' + stale-gallery + End + + It 'preserves the existing lookup and Windows JSON output' + OS_NAME=windows OFFER_NAME=WindowsServer SKU_NAME=windows-2022 SECURITY_TYPE_FEATURE=Standard + unset IMG_PUBLISHER IMG_OFFER IMG_SKU + export SIG_SOURCE_GALLERY_UNIQUE_NAME="$1" + AZ_STATUS=2 AZ_VERSIONS='' + + When call generate_publishing_info + The status should be success + The output should include 'COPY ME ---> https://example.invalid/images/test.vhd' + The contents of file "$AZ_CALLS" should include '' + The contents of file "${TEST_DIR}/stderr" should include 'mock Azure CLI lookup failed' + The value "$(read_metadata '.')" should equal '{"vhd_url":"https://example.invalid/images/test.vhd","os_name":"windows","sku_name":"windows-2022","offer_name":"WindowsServer","hyperv_generation":"V2","image_architecture":"x64","image_version":"202610.01.0","security_type_feature":"Standard","replication_inverse":"2"}' + End + End +End diff --git a/spec/vhdbuilder/packer/produce_packer_settings_logging_spec.sh b/spec/vhdbuilder/packer/produce_packer_settings_logging_spec.sh new file mode 100644 index 00000000000..28fba52967c --- /dev/null +++ b/spec/vhdbuilder/packer/produce_packer_settings_logging_spec.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# shellcheck disable=SC2329 + +Describe 'Packer settings source logging' + setup() { + SCRIPT_PATH="${PWD}/vhdbuilder/packer/produce-packer-settings.sh" + TEST_ROOT=$(mktemp -d "${SHELLSPEC_WORKDIR}/settings-logging.XXXXXX") + mkdir -p "${TEST_ROOT}/vhdbuilder/packer" + MODE=linuxVhdMode OS_SKU=AzureContainerLinux FEATURE_FLAGS=None ENVIRONMENT=tme + PACKER_BUILD_LOCATION=westus3 CVM_PACKER_BUILD_LOCATION=westeurope AZURE_LOCATION=eastus + SIG_SOURCE_GALLERY_UNIQUE_NAME=shared-acl-gallery + SIG_SOURCE_IMAGE_NAME=acl-3.0-amd64 SIG_SOURCE_IMAGE_VERSION=20260923.1209152.2 + } + BeforeEach 'setup' + + resolve_security_type_feature() { + SECURITY_TYPE_FEATURE=TrustedLaunch + } + + produce_ua_token() { + UA_TOKEN=test-only-token + } + + log_settings() ( + set -e + cd "$TEST_ROOT" + # Exercise the real CVM override and final settings generation without Azure setup. + eval "$(sed -n '/^if grep -q "cvm" /,/^fi$/p' "$SCRIPT_PATH")" + eval "$(sed -n '/^# set PACKER_BUILD_LOCATION /,$p' "$SCRIPT_PATH")" + ) + + Describe 'ACL builds' + Parameters + acl-3.0-amd64 20260923.1209152.2 None tme westus3 + acl-3.0-amd64 20260923.1209152.2 cvm tme westeurope + acl-3.0-arm64 20260923.1209110.1 None tme westus3 + acl-3.0-amd64 20260923.1209152.2 cvm prod eastus + End + + It "logs $1/$2 and the final $5 region before the settings dump" + SIG_SOURCE_IMAGE_NAME="$1" SIG_SOURCE_IMAGE_VERSION="$2" FEATURE_FLAGS="$3" ENVIRONMENT="$4" + expected=$(printf 'ACL base image: shared-acl-gallery/%s/%s\nPacker build region: %s\npacker settings:' "$1" "$2" "$5") + + When call log_settings + The status should be success + The output should include "$expected" + The output should not include test-only-token + The value "$(jq -r .location "${TEST_ROOT}/vhdbuilder/packer/settings.json")" should equal "$5" + End + End + + Describe 'other builds' + Parameters + linuxVhdMode Ubuntu + linuxVhdMode AzureLinux + linuxVhdMode AzureLinuxOSGuard + windowsVhdMode Windows + windowsVhdMode AzureContainerLinux + End + + It "does not add ACL source messages for $1/$2" + MODE="$1" OS_SKU="$2" + + When call log_settings + The status should be success + The output should include 'packer settings:' + The output should not include 'ACL base image:' + The output should not include 'Packer build region:' + The output should not include test-only-token + End + End +End diff --git a/spec/vhdbuilder/packer/test/linux_vhd_content_test_helpers_spec.sh b/spec/vhdbuilder/packer/test/linux_vhd_content_test_helpers_spec.sh index 86b443782df..8d0d025b5cf 100644 --- a/spec/vhdbuilder/packer/test/linux_vhd_content_test_helpers_spec.sh +++ b/spec/vhdbuilder/packer/test/linux_vhd_content_test_helpers_spec.sh @@ -1,7 +1,30 @@ #!/bin/bash # shellcheck disable=SC2329 -# ShellSpec tests for parseAutologinSessions helper function +# ShellSpec tests for content-test helpers and call sites + +Describe 'Linux content-test legacy parameters' + read_legacy_parameters() { + eval "$(sed -n '/^OS_VERSION=/,/^AGENTBAKER_REPOSITORY_URL=/p' vhdbuilder/packer/test/linux-vhd-content-test.sh)" + printf '<%s>\n' "$OS_VERSION" "$ENABLE_FIPS" "$OS_SKU" "$GIT_BRANCH" \ + "$IMG_SKU" "$FEATURE_FLAGS" "$GIT_COMMIT_HASH" "$AGENTBAKER_REPOSITORY_URL" + } + + Parameters + '' + 'https://github.com/example/AgentBaker.git' + End + + It 'accepts unprefixed values and retains the repository URL default' + arguments=(22.04 false Ubuntu refs/heads/main 22_04-lts-gen2 cvm commit) + if [ -n "$1" ]; then arguments+=("$1"); fi + expected_output=$(printf '<%s>\n' "${arguments[@]:0:7}" "${1:-https://github.com/Azure/AgentBaker.git}") + + When call read_legacy_parameters "${arguments[@]}" + The status should be success + The output should equal "$expected_output" + End +End Describe 'parseAutologinSessions helper function' # Extract only the function definition using sed - clean approach! @@ -233,3 +256,53 @@ Describe 'Inspektor Gadget version helper functions' End End End + +Describe 'testCronPermissions invocation' + BeforeAll "eval \"\$(sed -n '/^testCronPermissions()/,/^}/p' './vhdbuilder/packer/test/linux-vhd-content-test.sh')\"" + + checkPathPermissions() { + echo "checkPathPermissions:$2:$3:$4" + } + + checkPathDoesNotExist() { + echo "checkPathDoesNotExist:$2" + } + + runCronPermissionsTest() { + # Exercise argument expansion at the real call site without running the full VHD suite. + eval "$(sed -n '/^testCronPermissions /p' './vhdbuilder/packer/test/linux-vhd-content-test.sh')" + } + + Describe 'images exempt from cron checks' + Parameters + "" "AzureContainerLinux" + "azure-linux-3-acl" "AzureContainerLinux" + "" "Flatcar" + "minimal" "Ubuntu" + End + + It 'preserves the cron exemption at the call site' + IMG_SKU="$1" + OS_SKU="$2" + + When call runCronPermissionsTest + The status should be success + The output should include "testCronPermissions: Skipping cron file check" + The output should not include "checkPathPermissions:" + The output should not include "checkPathDoesNotExist:" + End + End + + It 'still checks cron paths for regular Ubuntu images' + IMG_SKU="22_04-lts-gen2" + OS_SKU="Ubuntu" + + When call runCronPermissionsTest + The status should be success + The output should include "checkPathPermissions:/etc/cron.allow:640:1" + The output should include "checkPathPermissions:/etc/cron.hourly:600:1" + The output should include "checkPathPermissions:/etc/crontab:600:0" + The output should include "checkPathDoesNotExist:/etc/cron.deny" + The output should not include "testCronPermissions: Skipping cron file check" + End +End diff --git a/spec/vhdbuilder/packer/test/run_test_spec.sh b/spec/vhdbuilder/packer/test/run_test_spec.sh index e014bdb0ba3..4ff4b294f55 100644 --- a/spec/vhdbuilder/packer/test/run_test_spec.sh +++ b/spec/vhdbuilder/packer/test/run_test_spec.sh @@ -43,3 +43,63 @@ Describe 'Linux content-test Run Command retries' fi End End + +Describe 'Linux content-test Run Command parameters' + az() { + while [ "$#" -gt 0 ] && [ "$1" != '--parameters' ]; do shift; done + [ "$#" -gt 0 ] || return 1 + shift + printf 'parameter-count=%s\n' "$#" + printf 'wire=<%s>\n' "$@" + + # Model Run Command dropping empty values before the VM script receives them. + local parameter + local received=() + for parameter in "$@"; do + if [ -n "$parameter" ]; then received+=("$parameter"); fi + done + set -- "${received[@]}" + local OS_VERSION='' ENABLE_FIPS='' OS_SKU='' GIT_BRANCH='' + local IMG_SKU='' FEATURE_FLAGS='' GIT_COMMIT_HASH='' AGENTBAKER_REPOSITORY_URL='' + eval "$(sed -n '/^OS_VERSION=/,/^AGENTBAKER_REPOSITORY_URL=/p' vhdbuilder/packer/test/linux-vhd-content-test.sh)" + printf 'decoded=<%s>\n' "$OS_VERSION" "$ENABLE_FIPS" "$OS_SKU" "$GIT_BRANCH" \ + "$IMG_SKU" "$FEATURE_FLAGS" "$GIT_COMMIT_HASH" "$AGENTBAKER_REPOSITORY_URL" + } + + run_parameter_round_trip() ( + set -eu + VM_NAME=test-vm TEST_VM_RESOURCE_GROUP_NAME=test-rg SCRIPT_PATH=test.sh + eval "$(sed -n '/^ for i in $(seq 1 3); do$/,/^ done$/p' vhdbuilder/packer/test/run-test.sh)" + printf '%s\n' "$ret" + ) + + Parameters + acl false AzureContainerLinux '' None + acl true AzureContainerLinux '' None + acl false AzureContainerLinux '' cvm + acl false AzureContainerLinux '' '' + 22.04 false Ubuntu 22_04-lts-gen2 '' + 22.04 true Ubuntu 22_04-lts-gen2 None + 3.0 false AzureLinux azurelinux-3-gen2 kata + 26.04 false Ubuntu minimal minimal,cvm + 22.04 false Ubuntu img-sku:literal feature-flags:literal + End + + It "preserves every field through transport for $3 with SKU '$4' and flags '$5'" + OS_VERSION="$1" ENABLE_FIPS="$2" OS_SKU="$3" IMG_SKU="$4" FEATURE_FLAGS="$5" + GIT_BRANCH=refs/heads/example + GIT_COMMIT_HASH=6da980562fefc854bb3a779f2414c068600065b1 + AGENTBAKER_REPOSITORY_URL=https://github.com/example/AgentBaker.git + expected_output=$( + printf 'parameter-count=8\n' + printf 'wire=<%s>\n' "$OS_VERSION" "$ENABLE_FIPS" "$OS_SKU" "$GIT_BRANCH" \ + "img-sku:$IMG_SKU" "feature-flags:$FEATURE_FLAGS" "$GIT_COMMIT_HASH" "$AGENTBAKER_REPOSITORY_URL" + printf 'decoded=<%s>\n' "$OS_VERSION" "$ENABLE_FIPS" "$OS_SKU" "$GIT_BRANCH" \ + "$IMG_SKU" "$FEATURE_FLAGS" "$GIT_COMMIT_HASH" "$AGENTBAKER_REPOSITORY_URL" + ) + + When run run_parameter_round_trip + The status should be success + The output should equal "$expected_output" + End +End diff --git a/vhdbuilder/packer/generate-vhd-publishing-info.sh b/vhdbuilder/packer/generate-vhd-publishing-info.sh index e9753831abf..40694376e64 100755 --- a/vhdbuilder/packer/generate-vhd-publishing-info.sh +++ b/vhdbuilder/packer/generate-vhd-publishing-info.sh @@ -13,8 +13,21 @@ required_env_vars=( # Higher the replication_inverse, lower is the usage and number of replicas set -x -PUBLISHER_BASE_IMAGE_VERSION=$(az vm image list -p ${IMG_PUBLISHER} -s ${IMG_SKU} --query "[?offer=='${IMG_OFFER}'].version" -o tsv --all | sort -u | tail -n 1) -echo "Latest ${IMG_PUBLISHER} base image version for offer ${IMG_OFFER} and sku ${IMG_SKU} is ${BASE_IMAGE_VERSION}" +if [ "${OS_NAME,,}" = "linux" ] && [ "${OFFER_NAME,,}" = "azurecontainerlinux" ]; then + for v in SIG_SOURCE_GALLERY_UNIQUE_NAME SIG_SOURCE_IMAGE_NAME SIG_SOURCE_IMAGE_VERSION; do + if [ -z "${!v}" ]; then + echo "WARNING: $v is not set for ACG source metadata; continuing" >&2 + fi + done + PUBLISHER_BASE_IMAGE_VERSION="${SIG_SOURCE_IMAGE_VERSION:-}" + # The legacy SKU field identifies the image definition for ACG sources. + PUBLISHER_BASE_IMAGE_SKU="${SIG_SOURCE_IMAGE_NAME:-}" + echo "ACG base image ${SIG_SOURCE_GALLERY_UNIQUE_NAME}/${SIG_SOURCE_IMAGE_NAME}/${SIG_SOURCE_IMAGE_VERSION}" +else + PUBLISHER_BASE_IMAGE_VERSION=$(az vm image list -p ${IMG_PUBLISHER} -s ${IMG_SKU} --query "[?offer=='${IMG_OFFER}'].version" -o tsv --all | sort -u | tail -n 1) + PUBLISHER_BASE_IMAGE_SKU="$IMG_SKU" + echo "Latest ${IMG_PUBLISHER} base image version for offer ${IMG_OFFER} and sku ${IMG_SKU} is ${PUBLISHER_BASE_IMAGE_VERSION}" +fi REPLICATION_INVERSE=1 @@ -104,7 +117,7 @@ if [ "${OS_NAME,,}" = "linux" ]; then "security_type_feature": "${SECURITY_TYPE_FEATURE}", "replication_inverse": "${REPLICATION_INVERSE}", "publisher_base_image_version": "${PUBLISHER_BASE_IMAGE_VERSION}", - "publisher_base_image_sku": "${IMG_SKU}" + "publisher_base_image_sku": "${PUBLISHER_BASE_IMAGE_SKU}" } EOF else diff --git a/vhdbuilder/packer/produce-packer-settings.sh b/vhdbuilder/packer/produce-packer-settings.sh index 248f63ed65d..11a67563433 100755 --- a/vhdbuilder/packer/produce-packer-settings.sh +++ b/vhdbuilder/packer/produce-packer-settings.sh @@ -247,6 +247,11 @@ cat < vhdbuilder/packer/settings.json } EOF +if [ "$MODE" = "linuxVhdMode" ] && [ "$OS_SKU" = "AzureContainerLinux" ]; then + echo "ACL base image: ${SIG_SOURCE_GALLERY_UNIQUE_NAME}/${SIG_SOURCE_IMAGE_NAME}/${SIG_SOURCE_IMAGE_VERSION}" + echo "Packer build region: ${PACKER_BUILD_LOCATION}" +fi + # so we don't accidently log UA_TOKEN, though ADO will automatically mask it if it appears in stdout # since it's coming from a variable group echo "packer settings:" diff --git a/vhdbuilder/packer/test/linux-vhd-content-test.sh b/vhdbuilder/packer/test/linux-vhd-content-test.sh index 66fb3f438a5..2675f173786 100644 --- a/vhdbuilder/packer/test/linux-vhd-content-test.sh +++ b/vhdbuilder/packer/test/linux-vhd-content-test.sh @@ -15,8 +15,9 @@ OS_VERSION="$1" ENABLE_FIPS="$2" OS_SKU="$3" GIT_BRANCH="$4" -IMG_SKU="$5" -FEATURE_FLAGS="$6" +# Strip transport prefixes while accepting legacy unprefixed arguments. +IMG_SKU="${5#img-sku:}" +FEATURE_FLAGS="${6#feature-flags:}" GIT_COMMIT_HASH="$7" AGENTBAKER_REPOSITORY_URL="${8:-https://github.com/Azure/AgentBaker.git}" @@ -605,6 +606,9 @@ testChrony() { local test="testChrony" echo "$test:Start" + # Known issue: ACL CVM can fail Chrony validation; keep these checks enabled. + # Base-image fix: https://github.com/microsoft/azure-container-linux/pull/61. + # ---- Test Setup ---- # Test ntp is not active status=$(systemctl show -p SubState --value ntp) @@ -2834,7 +2838,7 @@ testCustomCATrustNodeCAWatcherRetagged testLoginDefs testUserAdd testNetworkSettings -testCronPermissions $IMG_SKU $OS_SKU +testCronPermissions "$IMG_SKU" "$OS_SKU" testCoreDumpSettings testNfsServerService testPamDSettings $OS_SKU $OS_VERSION diff --git a/vhdbuilder/packer/test/run-test.sh b/vhdbuilder/packer/test/run-test.sh index afa79436def..17fb803cc76 100755 --- a/vhdbuilder/packer/test/run-test.sh +++ b/vhdbuilder/packer/test/run-test.sh @@ -173,12 +173,13 @@ if [ "${OS_TYPE,,}" = "linux" ]; then GIT_COMMIT_HASH="${GIT_COMMIT_HASH:-$(git rev-parse HEAD)}" AGENTBAKER_REPOSITORY_URL="${AGENTBAKER_REPOSITORY_URL:-https://github.com/Azure/AgentBaker.git}" SCRIPT_PATH="$CDIR/$LINUX_SCRIPT_PATH" + # Prefix optional values so Run Command does not drop empty positional arguments. for i in $(seq 1 3); do ret=$(az vm run-command invoke --command-id RunShellScript \ --name "$VM_NAME" \ --resource-group "$TEST_VM_RESOURCE_GROUP_NAME" \ --scripts "@$SCRIPT_PATH" \ - --parameters "${OS_VERSION}" "${ENABLE_FIPS}" "${OS_SKU}" "${GIT_BRANCH}" "${IMG_SKU}" "${FEATURE_FLAGS}" "${GIT_COMMIT_HASH}" "${AGENTBAKER_REPOSITORY_URL}") && break + --parameters "${OS_VERSION}" "${ENABLE_FIPS}" "${OS_SKU}" "${GIT_BRANCH}" "img-sku:${IMG_SKU}" "feature-flags:${FEATURE_FLAGS}" "${GIT_COMMIT_HASH}" "${AGENTBAKER_REPOSITORY_URL}") && break if [ "$i" -eq 3 ]; then echo "Linux content-test Run Command failed after ${i} attempts." >&2 exit 1 diff --git a/vhdbuilder/packer/vhd-image-builder-acl-arm64.json b/vhdbuilder/packer/vhd-image-builder-acl-arm64.json index 949c37200ad..e1045ae1ca1 100644 --- a/vhdbuilder/packer/vhd-image-builder-acl-arm64.json +++ b/vhdbuilder/packer/vhd-image-builder-acl-arm64.json @@ -19,10 +19,10 @@ "container_runtime": "{{env `CONTAINER_RUNTIME`}}", "captured_sig_version": "{{env `CAPTURED_SIG_VERSION`}}", "enable_fips": "{{env `ENABLE_FIPS`}}", - "img_publisher": "{{env `IMG_PUBLISHER`}}", - "img_offer": "{{env `IMG_OFFER`}}", - "img_sku": "{{env `IMG_SKU`}}", - "img_version": "{{env `IMG_VERSION`}}", + "img_sku": "", + "sig_source_gallery_unique_name": "{{env `SIG_SOURCE_GALLERY_UNIQUE_NAME`}}", + "sig_source_image_name": "{{env `SIG_SOURCE_IMAGE_NAME`}}", + "sig_source_image_version": "{{env `SIG_SOURCE_IMAGE_VERSION`}}", "vnet_resource_group_name": "{{env `VNET_RESOURCE_GROUP_NAME`}}", "vnet_name": "{{env `VNET_NAME`}}", "subnet_name": "{{env `SUBNET_NAME`}}", @@ -41,10 +41,9 @@ "ssh_read_write_timeout": "5m", "os_type": "Linux", "os_disk_size_gb": 60, - "image_publisher": "{{user `img_publisher`}}", - "image_offer": "{{user `img_offer`}}", - "image_sku": "{{user `img_sku`}}", - "image_version": "{{user `img_version`}}", + "shared_image_gallery": { + "direct_shared_gallery_image_id": "/SharedGalleries/{{user `sig_source_gallery_unique_name`}}/Images/{{user `sig_source_image_name`}}/Versions/{{user `sig_source_image_version`}}" + }, "azure_tags": { "buildDefinitionName": "{{user `build_definition_name`}}", "buildNumber": "{{user `build_number`}}", diff --git a/vhdbuilder/packer/vhd-image-builder-acl.json b/vhdbuilder/packer/vhd-image-builder-acl.json index b1f3a08bd53..4bc0ebb18c9 100644 --- a/vhdbuilder/packer/vhd-image-builder-acl.json +++ b/vhdbuilder/packer/vhd-image-builder-acl.json @@ -19,10 +19,10 @@ "container_runtime": "{{env `CONTAINER_RUNTIME`}}", "captured_sig_version": "{{env `CAPTURED_SIG_VERSION`}}", "enable_fips": "{{env `ENABLE_FIPS`}}", - "img_publisher": "{{env `IMG_PUBLISHER`}}", - "img_offer": "{{env `IMG_OFFER`}}", - "img_sku": "{{env `IMG_SKU`}}", - "img_version": "{{env `IMG_VERSION`}}", + "img_sku": "", + "sig_source_gallery_unique_name": "{{env `SIG_SOURCE_GALLERY_UNIQUE_NAME`}}", + "sig_source_image_name": "{{env `SIG_SOURCE_IMAGE_NAME`}}", + "sig_source_image_version": "{{env `SIG_SOURCE_IMAGE_VERSION`}}", "vnet_resource_group_name": "{{env `VNET_RESOURCE_GROUP_NAME`}}", "vnet_name": "{{env `VNET_NAME`}}", "subnet_name": "{{env `SUBNET_NAME`}}", @@ -41,10 +41,9 @@ "ssh_read_write_timeout": "5m", "os_type": "Linux", "os_disk_size_gb": 60, - "image_publisher": "{{user `img_publisher`}}", - "image_offer": "{{user `img_offer`}}", - "image_sku": "{{user `img_sku`}}", - "image_version": "{{user `img_version`}}", + "shared_image_gallery": { + "direct_shared_gallery_image_id": "/SharedGalleries/{{user `sig_source_gallery_unique_name`}}/Images/{{user `sig_source_image_name`}}/Versions/{{user `sig_source_image_version`}}" + }, "azure_tags": { "buildDefinitionName": "{{user `build_definition_name`}}", "buildNumber": "{{user `build_number`}}",