From 0e0e742fdcda4ed85bfcac7302df79c40e27dce5 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 3 Sep 2026 16:37:52 -0400 Subject: [PATCH 01/47] fix --- .pipelines/.vsts-vhd-builder-release.yaml | 28 +++++++++++++++++++++++ pkg/agent/bakerapi_test.go | 1 + pkg/agent/datamodel/sig_config.go | 11 +++++++++ pkg/agent/datamodel/sig_config_test.go | 9 +++++++- pkg/agent/datamodel/types.go | 2 ++ pkg/agent/datamodel/types_test.go | 7 ++++++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index a73d30d0947..d0ab0b62518 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -125,6 +125,10 @@ parameters: displayName: Build 2604 Minimal ARM64 Gen2 Containerd type: boolean default: true + - name: build2604cvmgen2containerd + displayName: Build 2604 CVM Gen2 Containerd + type: boolean + default: true - name: buildacltlgen2 displayName: Build Azure Container Linux TL Gen2 type: boolean @@ -811,6 +815,30 @@ stages: useOverrides: ${{ parameters.useOverrides }} overrideBranch: ${{ parameters.overrideBranch }} artifactName: 2604-minimal-arm64-gen2-containerd + - job: build2604cvmgen2containerd + condition: eq('${{ parameters.build2604cvmgen2containerd }}', true) + dependsOn: [ ] + timeoutInMinutes: 360 + steps: + - bash: | + echo '##vso[task.setvariable variable=OS_SKU]Ubuntu' + echo '##vso[task.setvariable variable=OS_VERSION]26.04' + echo '##vso[task.setvariable variable=IMG_PUBLISHER]Canonical' + echo '##vso[task.setvariable variable=IMG_OFFER]ubuntu-26_04-lts' + echo '##vso[task.setvariable variable=IMG_SKU]server-cvm' + echo '##vso[task.setvariable variable=IMG_VERSION]latest' + echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' + echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_DC16ads_v5' + echo '##vso[task.setvariable variable=FEATURE_FLAGS]cvm' + echo '##vso[task.setvariable variable=ARCHITECTURE]X86_64' + echo '##vso[task.setvariable variable=ENABLE_FIPS]False' + echo '##vso[task.setvariable variable=ENABLE_TRUSTED_LAUNCH]False' + displayName: Setup Build Variables + - template: ./templates/.builder-release-template.yaml + parameters: + useOverrides: ${{ parameters.useOverrides }} + overrideBranch: ${{ parameters.overrideBranch }} + artifactName: 2604-cvm-gen2-containerd - job: buildacltlgen2 condition: eq('${{ parameters.buildacltlgen2 }}', true) dependsOn: [ ] diff --git a/pkg/agent/bakerapi_test.go b/pkg/agent/bakerapi_test.go index 69b853285ff..448a16fdbf4 100644 --- a/pkg/agent/bakerapi_test.go +++ b/pkg/agent/bakerapi_test.go @@ -377,6 +377,7 @@ var _ = Describe("AgentBaker API implementation tests", func() { datamodel.AKSUbuntuArm64Containerd2404Gen2, datamodel.AKSUbuntuContainerd2404, datamodel.AKSUbuntuContainerd2404TLGen2, + datamodel.AKSUbuntuContainerd2604CVMGen2, } marinerDistros = []datamodel.Distro{ diff --git a/pkg/agent/datamodel/sig_config.go b/pkg/agent/datamodel/sig_config.go index e9bbdcc4256..0e7dfdad6b3 100644 --- a/pkg/agent/datamodel/sig_config.go +++ b/pkg/agent/datamodel/sig_config.go @@ -113,6 +113,7 @@ var AvailableUbuntu2404Distros = []Distro{ var AvailableUbuntu2604Distros = []Distro{ AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, + AKSUbuntuContainerd2604CVMGen2, } //nolint:gochecknoglobals @@ -173,6 +174,7 @@ var AvailableContainerdDistros = []Distro{ AKSAzureLinuxV3OSGuardGen2FIPSTL, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, + AKSUbuntuContainerd2604CVMGen2, } //nolint:gochecknoglobals @@ -194,6 +196,7 @@ var AvailableGen2Distros = []Distro{ AKSUbuntuContainerd2404TLGen2, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, + AKSUbuntuContainerd2604CVMGen2, AKSFlatcarGen2, AKSFlatcarArm64Gen2, AKSACLGen2TL, @@ -607,6 +610,13 @@ var ( Version: LinuxSIGImageVersion, } + SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate = SigImageConfigTemplate{ + ResourceGroup: AKSUbuntuResourceGroup, + Gallery: AKSUbuntuGalleryName, + Definition: "2604gen2CVMcontainerd", + Version: LinuxSIGImageVersion, + } + SIGCBLMarinerV1ImageConfigTemplate = SigImageConfigTemplate{ ResourceGroup: AKSCBLMarinerResourceGroup, Gallery: AKSCBLMarinerGalleryName, @@ -947,6 +957,7 @@ func getSigUbuntuImageConfigMapWithOpts(opts ...SigImageConfigOpt) map[Distro]Si AKSUbuntuContainerd2404TLGen2: SIGUbuntuContainerd2404TLGen2ImageConfigTemplate.WithOptions(opts...), AKSUbuntuMinimalContainerd2604Gen2: SIGUbuntuMinimalContainerd2604Gen2ImageConfigTemplate.WithOptions(opts...), AKSUbuntuMinimalArm64Containerd2604Gen2: SIGUbuntuMinimalArm64Containerd2604Gen2ImageConfigTemplate.WithOptions(opts...), + AKSUbuntuContainerd2604CVMGen2: SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate.WithOptions(opts...), } } diff --git a/pkg/agent/datamodel/sig_config_test.go b/pkg/agent/datamodel/sig_config_test.go index 6528e61bead..3ac2aa727d8 100644 --- a/pkg/agent/datamodel/sig_config_test.go +++ b/pkg/agent/datamodel/sig_config_test.go @@ -25,6 +25,7 @@ var _ = Describe("GetMaintainedLinuxSIGImageConfigMap", func() { AKSUbuntuContainerd2404TLGen2: SIGUbuntuContainerd2404TLGen2ImageConfigTemplate.WithOptions(), AKSUbuntuMinimalContainerd2604Gen2: SIGUbuntuMinimalContainerd2604Gen2ImageConfigTemplate.WithOptions(), AKSUbuntuMinimalArm64Containerd2604Gen2: SIGUbuntuMinimalArm64Containerd2604Gen2ImageConfigTemplate.WithOptions(), + AKSUbuntuContainerd2604CVMGen2: SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3: SIGAzureLinuxV3Gen1ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3Gen2: SIGAzureLinuxV3Gen2ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3FIPS: SIGAzureLinuxV3Gen1FIPSImageConfigTemplate.WithOptions(), @@ -96,7 +97,7 @@ var _ = Describe("GetSIGAzureCloudSpecConfig", func() { Expect(sigConfig.SigTenantID).To(Equal("sometenantid")) Expect(sigConfig.SubscriptionID).To(Equal("somesubid")) - Expect(len(sigConfig.SigUbuntuImageConfig)).To(Equal(19)) + Expect(len(sigConfig.SigUbuntuImageConfig)).To(Equal(20)) Expect(len(sigConfig.SigCBLMarinerImageConfig)).To(Equal(9)) @@ -408,6 +409,12 @@ var _ = Describe("GetSIGAzureCloudSpecConfig", func() { Expect(aksUbuntuArm64MinimalContainerd2604Gen2.Definition).To(Equal("2604minimalgen2arm64containerd")) Expect(aksUbuntuArm64MinimalContainerd2604Gen2.Version).To(Equal(LinuxSIGImageVersion)) + aksUbuntu2604CVMGen2Containerd := sigConfig.SigUbuntuImageConfig[AKSUbuntuContainerd2604CVMGen2] + Expect(aksUbuntu2604CVMGen2Containerd.ResourceGroup).To(Equal("resourcegroup")) + Expect(aksUbuntu2604CVMGen2Containerd.Gallery).To(Equal("aksubuntu")) + Expect(aksUbuntu2604CVMGen2Containerd.Definition).To(Equal("2604gen2CVMcontainerd")) + Expect(aksUbuntu2604CVMGen2Containerd.Version).To(Equal(LinuxSIGImageVersion)) + aclGen2 := sigConfig.SigAzureLinuxImageConfig[AKSACLGen2TL] Expect(aclGen2.ResourceGroup).To(Equal("resourcegroup")) Expect(aclGen2.Gallery).To(Equal("aksazurelinux")) diff --git a/pkg/agent/datamodel/types.go b/pkg/agent/datamodel/types.go index a96a1af1c43..8703c1e0037 100644 --- a/pkg/agent/datamodel/types.go +++ b/pkg/agent/datamodel/types.go @@ -194,6 +194,7 @@ const ( AKSUbuntuContainerd2404Gen2 Distro = "aks-ubuntu-containerd-24.04-gen2" AKSUbuntuMinimalContainerd2604Gen2 Distro = "aks-ubuntu-minimal-containerd-26.04-gen2" AKSUbuntuMinimalArm64Containerd2604Gen2 Distro = "aks-ubuntu-minimal-arm64-containerd-26.04-gen2" + AKSUbuntuContainerd2604CVMGen2 Distro = "aks-ubuntu-containerd-26.04-cvm-gen2" AKSAzureLinuxV3CVMGen2 Distro = "aks-azurelinux-v3-cvm-gen2" AKSUbuntuContainerd2404TLGen2 Distro = "aks-ubuntu-containerd-24.04-tl-gen2" AKSFlatcarGen2 Distro = "aks-flatcar-gen2" @@ -287,6 +288,7 @@ var AKSDistrosAvailableOnVHD = []Distro{ AKSUbuntuContainerd2404TLGen2, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, + AKSUbuntuContainerd2604CVMGen2, AKSFlatcarGen2, AKSFlatcarArm64Gen2, AKSACLGen2TL, diff --git a/pkg/agent/datamodel/types_test.go b/pkg/agent/datamodel/types_test.go index d666f12cbdb..21239c90595 100644 --- a/pkg/agent/datamodel/types_test.go +++ b/pkg/agent/datamodel/types_test.go @@ -1050,6 +1050,13 @@ func TestAgentPoolProfileIs2604VHDDistro(t *testing.T) { }, expected: true, }, + { + name: "26.04 CVM Gen2 VHD distro", + ap: AgentPoolProfile{ + Distro: AKSUbuntuContainerd2604CVMGen2, + }, + expected: true, + }, { name: "24.04 VHD distro is not a 2604 distro", ap: AgentPoolProfile{ From c2a5a266aa20d27db1373d91ea1cba364509c0f3 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 3 Sep 2026 17:22:15 -0400 Subject: [PATCH 02/47] fix --- vhdbuilder/packer/post-install-dependencies.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vhdbuilder/packer/post-install-dependencies.sh b/vhdbuilder/packer/post-install-dependencies.sh index 72509dae75c..1ea59b2a8c8 100644 --- a/vhdbuilder/packer/post-install-dependencies.sh +++ b/vhdbuilder/packer/post-install-dependencies.sh @@ -56,7 +56,11 @@ if [ $OS = $UBUNTU_OS_NAME ]; then # -> node never joins. Purging it also drops packagekit-tools + software-properties-common (add-apt-repository, # unused at node runtime; the build's only add-apt-repository usage is earlier in pre-install-dependencies.sh). # No-op on the minimal image, which does not ship these. - retrycmd_if_failure 10 2 60 apt-get purge --auto-remove packagekit packagekit-tools software-properties-common -y || exit 1 + packagekit_packages=(packagekit software-properties-common) + if [ "$UBUNTU_RELEASE" != "26.04" ]; then + packagekit_packages+=(packagekit-tools) + fi + retrycmd_if_failure 10 2 60 apt-get purge --auto-remove "${packagekit_packages[@]}" -y || exit 1 fi # strip old kernels/packages From 2e171fefdae95ca6950e35fba1fedfd319356fe6 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 11 Sep 2026 17:59:03 -0400 Subject: [PATCH 03/47] fix --- .pipelines/.vsts-vhd-builder-release.yaml | 14 +++++++------- pkg/agent/bakerapi_test.go | 2 +- pkg/agent/datamodel/sig_config.go | 12 ++++++------ pkg/agent/datamodel/sig_config_test.go | 12 ++++++------ pkg/agent/datamodel/types.go | 4 ++-- pkg/agent/datamodel/types_test.go | 4 ++-- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index d0ab0b62518..553358e4399 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -125,8 +125,8 @@ parameters: displayName: Build 2604 Minimal ARM64 Gen2 Containerd type: boolean default: true - - name: build2604cvmgen2containerd - displayName: Build 2604 CVM Gen2 Containerd + - name: build2604minimalcvmgen2containerd + displayName: Build 2604 Minimal CVM Gen2 Containerd type: boolean default: true - name: buildacltlgen2 @@ -815,8 +815,8 @@ stages: useOverrides: ${{ parameters.useOverrides }} overrideBranch: ${{ parameters.overrideBranch }} artifactName: 2604-minimal-arm64-gen2-containerd - - job: build2604cvmgen2containerd - condition: eq('${{ parameters.build2604cvmgen2containerd }}', true) + - job: build2604minimalcvmgen2containerd + condition: eq('${{ parameters.build2604minimalcvmgen2containerd }}', true) dependsOn: [ ] timeoutInMinutes: 360 steps: @@ -825,11 +825,11 @@ stages: echo '##vso[task.setvariable variable=OS_VERSION]26.04' echo '##vso[task.setvariable variable=IMG_PUBLISHER]Canonical' echo '##vso[task.setvariable variable=IMG_OFFER]ubuntu-26_04-lts' - echo '##vso[task.setvariable variable=IMG_SKU]server-cvm' + echo '##vso[task.setvariable variable=IMG_SKU]minimal' echo '##vso[task.setvariable variable=IMG_VERSION]latest' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_DC16ads_v5' - echo '##vso[task.setvariable variable=FEATURE_FLAGS]cvm' + echo '##vso[task.setvariable variable=FEATURE_FLAGS]minimal,cvm' echo '##vso[task.setvariable variable=ARCHITECTURE]X86_64' echo '##vso[task.setvariable variable=ENABLE_FIPS]False' echo '##vso[task.setvariable variable=ENABLE_TRUSTED_LAUNCH]False' @@ -838,7 +838,7 @@ stages: parameters: useOverrides: ${{ parameters.useOverrides }} overrideBranch: ${{ parameters.overrideBranch }} - artifactName: 2604-cvm-gen2-containerd + artifactName: 2604-minimal-cvm-gen2-containerd - job: buildacltlgen2 condition: eq('${{ parameters.buildacltlgen2 }}', true) dependsOn: [ ] diff --git a/pkg/agent/bakerapi_test.go b/pkg/agent/bakerapi_test.go index 448a16fdbf4..1b328b400b0 100644 --- a/pkg/agent/bakerapi_test.go +++ b/pkg/agent/bakerapi_test.go @@ -377,7 +377,7 @@ var _ = Describe("AgentBaker API implementation tests", func() { datamodel.AKSUbuntuArm64Containerd2404Gen2, datamodel.AKSUbuntuContainerd2404, datamodel.AKSUbuntuContainerd2404TLGen2, - datamodel.AKSUbuntuContainerd2604CVMGen2, + datamodel.AKSUbuntuMinimalContainerd2604CVMGen2, } marinerDistros = []datamodel.Distro{ diff --git a/pkg/agent/datamodel/sig_config.go b/pkg/agent/datamodel/sig_config.go index c6416044500..dc56d204ff4 100644 --- a/pkg/agent/datamodel/sig_config.go +++ b/pkg/agent/datamodel/sig_config.go @@ -113,7 +113,7 @@ var AvailableUbuntu2404Distros = []Distro{ var AvailableUbuntu2604Distros = []Distro{ AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, - AKSUbuntuContainerd2604CVMGen2, + AKSUbuntuMinimalContainerd2604CVMGen2, } //nolint:gochecknoglobals @@ -174,7 +174,7 @@ var AvailableContainerdDistros = []Distro{ AKSAzureLinuxV3OSGuardGen2FIPSTL, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, - AKSUbuntuContainerd2604CVMGen2, + AKSUbuntuMinimalContainerd2604CVMGen2, } //nolint:gochecknoglobals @@ -196,7 +196,7 @@ var AvailableGen2Distros = []Distro{ AKSUbuntuContainerd2404TLGen2, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, - AKSUbuntuContainerd2604CVMGen2, + AKSUbuntuMinimalContainerd2604CVMGen2, AKSFlatcarGen2, AKSFlatcarArm64Gen2, AKSACLGen2TL, @@ -613,10 +613,10 @@ var ( Version: LinuxSIGImageVersion, } - SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate = SigImageConfigTemplate{ + SIGUbuntuMinimalContainerd2604CVMGen2ImageConfigTemplate = SigImageConfigTemplate{ ResourceGroup: AKSUbuntuResourceGroup, Gallery: AKSUbuntuGalleryName, - Definition: "2604gen2CVMcontainerd", + Definition: "2604minimalgen2CVMcontainerd", Version: LinuxSIGImageVersion, } @@ -960,7 +960,7 @@ func getSigUbuntuImageConfigMapWithOpts(opts ...SigImageConfigOpt) map[Distro]Si AKSUbuntuContainerd2404TLGen2: SIGUbuntuContainerd2404TLGen2ImageConfigTemplate.WithOptions(opts...), AKSUbuntuMinimalContainerd2604Gen2: SIGUbuntuMinimalContainerd2604Gen2ImageConfigTemplate.WithOptions(opts...), AKSUbuntuMinimalArm64Containerd2604Gen2: SIGUbuntuMinimalArm64Containerd2604Gen2ImageConfigTemplate.WithOptions(opts...), - AKSUbuntuContainerd2604CVMGen2: SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate.WithOptions(opts...), + AKSUbuntuMinimalContainerd2604CVMGen2: SIGUbuntuMinimalContainerd2604CVMGen2ImageConfigTemplate.WithOptions(opts...), } } diff --git a/pkg/agent/datamodel/sig_config_test.go b/pkg/agent/datamodel/sig_config_test.go index b1b3ad4b566..42109046b26 100644 --- a/pkg/agent/datamodel/sig_config_test.go +++ b/pkg/agent/datamodel/sig_config_test.go @@ -25,7 +25,7 @@ var _ = Describe("GetMaintainedLinuxSIGImageConfigMap", func() { AKSUbuntuContainerd2404TLGen2: SIGUbuntuContainerd2404TLGen2ImageConfigTemplate.WithOptions(), AKSUbuntuMinimalContainerd2604Gen2: SIGUbuntuMinimalContainerd2604Gen2ImageConfigTemplate.WithOptions(), AKSUbuntuMinimalArm64Containerd2604Gen2: SIGUbuntuMinimalArm64Containerd2604Gen2ImageConfigTemplate.WithOptions(), - AKSUbuntuContainerd2604CVMGen2: SIGUbuntuContainerd2604CVMGen2ImageConfigTemplate.WithOptions(), + AKSUbuntuMinimalContainerd2604CVMGen2: SIGUbuntuMinimalContainerd2604CVMGen2ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3: SIGAzureLinuxV3Gen1ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3Gen2: SIGAzureLinuxV3Gen2ImageConfigTemplate.WithOptions(), AKSAzureLinuxV3FIPS: SIGAzureLinuxV3Gen1FIPSImageConfigTemplate.WithOptions(), @@ -408,11 +408,11 @@ var _ = Describe("GetSIGAzureCloudSpecConfig", func() { Expect(aksUbuntuArm64MinimalContainerd2604Gen2.Definition).To(Equal("2604minimalgen2arm64containerd")) Expect(aksUbuntuArm64MinimalContainerd2604Gen2.Version).To(Equal(LinuxSIGImageVersion)) - aksUbuntu2604CVMGen2Containerd := sigConfig.SigUbuntuImageConfig[AKSUbuntuContainerd2604CVMGen2] - Expect(aksUbuntu2604CVMGen2Containerd.ResourceGroup).To(Equal("resourcegroup")) - Expect(aksUbuntu2604CVMGen2Containerd.Gallery).To(Equal("aksubuntu")) - Expect(aksUbuntu2604CVMGen2Containerd.Definition).To(Equal("2604gen2CVMcontainerd")) - Expect(aksUbuntu2604CVMGen2Containerd.Version).To(Equal(LinuxSIGImageVersion)) + aksUbuntu2604MinimalCVMGen2Containerd := sigConfig.SigUbuntuImageConfig[AKSUbuntuMinimalContainerd2604CVMGen2] + Expect(aksUbuntu2604MinimalCVMGen2Containerd.ResourceGroup).To(Equal("resourcegroup")) + Expect(aksUbuntu2604MinimalCVMGen2Containerd.Gallery).To(Equal("aksubuntu")) + Expect(aksUbuntu2604MinimalCVMGen2Containerd.Definition).To(Equal("2604minimalgen2CVMcontainerd")) + Expect(aksUbuntu2604MinimalCVMGen2Containerd.Version).To(Equal(LinuxSIGImageVersion)) aclGen2 := sigConfig.SigAzureLinuxImageConfig[AKSACLGen2TL] Expect(aclGen2.ResourceGroup).To(Equal("resourcegroup")) diff --git a/pkg/agent/datamodel/types.go b/pkg/agent/datamodel/types.go index 8703c1e0037..5481c13d82d 100644 --- a/pkg/agent/datamodel/types.go +++ b/pkg/agent/datamodel/types.go @@ -194,7 +194,7 @@ const ( AKSUbuntuContainerd2404Gen2 Distro = "aks-ubuntu-containerd-24.04-gen2" AKSUbuntuMinimalContainerd2604Gen2 Distro = "aks-ubuntu-minimal-containerd-26.04-gen2" AKSUbuntuMinimalArm64Containerd2604Gen2 Distro = "aks-ubuntu-minimal-arm64-containerd-26.04-gen2" - AKSUbuntuContainerd2604CVMGen2 Distro = "aks-ubuntu-containerd-26.04-cvm-gen2" + AKSUbuntuMinimalContainerd2604CVMGen2 Distro = "aks-ubuntu-minimal-containerd-26.04-cvm-gen2" AKSAzureLinuxV3CVMGen2 Distro = "aks-azurelinux-v3-cvm-gen2" AKSUbuntuContainerd2404TLGen2 Distro = "aks-ubuntu-containerd-24.04-tl-gen2" AKSFlatcarGen2 Distro = "aks-flatcar-gen2" @@ -288,7 +288,7 @@ var AKSDistrosAvailableOnVHD = []Distro{ AKSUbuntuContainerd2404TLGen2, AKSUbuntuMinimalContainerd2604Gen2, AKSUbuntuMinimalArm64Containerd2604Gen2, - AKSUbuntuContainerd2604CVMGen2, + AKSUbuntuMinimalContainerd2604CVMGen2, AKSFlatcarGen2, AKSFlatcarArm64Gen2, AKSACLGen2TL, diff --git a/pkg/agent/datamodel/types_test.go b/pkg/agent/datamodel/types_test.go index 21239c90595..a89c7e64394 100644 --- a/pkg/agent/datamodel/types_test.go +++ b/pkg/agent/datamodel/types_test.go @@ -1051,9 +1051,9 @@ func TestAgentPoolProfileIs2604VHDDistro(t *testing.T) { expected: true, }, { - name: "26.04 CVM Gen2 VHD distro", + name: "26.04 minimal CVM Gen2 VHD distro", ap: AgentPoolProfile{ - Distro: AKSUbuntuContainerd2604CVMGen2, + Distro: AKSUbuntuMinimalContainerd2604CVMGen2, }, expected: true, }, From 8bb8798acb84e48086608427db1501b4ba52ad80 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 11:39:29 -0400 Subject: [PATCH 04/47] fix --- vhdbuilder/packer/post-install-dependencies.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vhdbuilder/packer/post-install-dependencies.sh b/vhdbuilder/packer/post-install-dependencies.sh index 1ea59b2a8c8..72509dae75c 100644 --- a/vhdbuilder/packer/post-install-dependencies.sh +++ b/vhdbuilder/packer/post-install-dependencies.sh @@ -56,11 +56,7 @@ if [ $OS = $UBUNTU_OS_NAME ]; then # -> node never joins. Purging it also drops packagekit-tools + software-properties-common (add-apt-repository, # unused at node runtime; the build's only add-apt-repository usage is earlier in pre-install-dependencies.sh). # No-op on the minimal image, which does not ship these. - packagekit_packages=(packagekit software-properties-common) - if [ "$UBUNTU_RELEASE" != "26.04" ]; then - packagekit_packages+=(packagekit-tools) - fi - retrycmd_if_failure 10 2 60 apt-get purge --auto-remove "${packagekit_packages[@]}" -y || exit 1 + retrycmd_if_failure 10 2 60 apt-get purge --auto-remove packagekit packagekit-tools software-properties-common -y || exit 1 fi # strip old kernels/packages From da0ab43621413c53a9df4b855a1af639510c1f8f Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 14:43:28 -0400 Subject: [PATCH 05/47] fix --- .pipelines/.vsts-vhd-builder-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index 553358e4399..234dff8c499 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -825,7 +825,7 @@ stages: echo '##vso[task.setvariable variable=OS_VERSION]26.04' echo '##vso[task.setvariable variable=IMG_PUBLISHER]Canonical' echo '##vso[task.setvariable variable=IMG_OFFER]ubuntu-26_04-lts' - echo '##vso[task.setvariable variable=IMG_SKU]minimal' + echo '##vso[task.setvariable variable=IMG_SKU]server-cvm' echo '##vso[task.setvariable variable=IMG_VERSION]latest' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_DC16ads_v5' From 2e5faf9158ecc201f7eeda4b604c085e706ff2f5 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 14:59:00 -0400 Subject: [PATCH 06/47] fix --- .pipelines/.vsts-vhd-builder-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index 234dff8c499..0eaa37a2cde 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -829,7 +829,7 @@ stages: echo '##vso[task.setvariable variable=IMG_VERSION]latest' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_DC16ads_v5' - echo '##vso[task.setvariable variable=FEATURE_FLAGS]minimal,cvm' + echo '##vso[task.setvariable variable=FEATURE_FLAGS]cvm' echo '##vso[task.setvariable variable=ARCHITECTURE]X86_64' echo '##vso[task.setvariable variable=ENABLE_FIPS]False' echo '##vso[task.setvariable variable=ENABLE_TRUSTED_LAUNCH]False' From 91fd8205067762f6777cc0737915979ac52bfb21 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 16:39:15 -0400 Subject: [PATCH 07/47] fix --- .../templates/.builder-release-template.yaml | 2 +- .../artifacts/ubuntu/cse_install_ubuntu.sh | 2 +- .../packer/prune_server_cvm_spec.sh | 99 ++++++ vhdbuilder/packer/pre-install-dependencies.sh | 16 +- .../ubuntu-2604-cvm/candidate-packages.txt | 332 ++++++++++++++++++ .../ubuntu-2604-cvm/prune-server-cvm.sh | 266 ++++++++++++++ .../ubuntu-2604-cvm/required-packages.txt | 23 ++ vhdbuilder/packer/vhd-image-builder-cvm.json | 15 + 8 files changed, 751 insertions(+), 4 deletions(-) create mode 100644 spec/vhdbuilder/packer/prune_server_cvm_spec.sh create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 7069f8d5ed7..24a9b3f60cd 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -76,6 +76,7 @@ steps: - bash: | SKU_NAME=${OS_VERSION} && \ if grep -q "minimal" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}minimal"; fi && \ + if [[ "${OS_SKU}" == "Ubuntu" && "${OS_VERSION}" == "26.04" && "${IMG_SKU}" == "server-cvm" ]] && tr ',' '\n' <<<"${FEATURE_FLAGS}" | grep -Fxq "cvm"; then SKU_NAME="${SKU_NAME}minimal"; fi && \ if [[ "${HYPERV_GENERATION,,}" == "v2" ]]; then SKU_NAME="${SKU_NAME}gen2"; fi && \ if [[ "${ARCHITECTURE,,}" == "arm64" ]]; then SKU_NAME="${SKU_NAME}arm64"; fi && \ if grep -q "NVIDIA_GB" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}gb"; fi && \ @@ -97,7 +98,6 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) diff --git a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh index 3392f028fe9..1ade5582bf4 100755 --- a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh +++ b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh @@ -108,7 +108,7 @@ installDeps() { pkg_list=(apparmor-utils bind9-dnsutils ca-certificates ceph-common cgroup-lite cifs-utils conntrack cracklib-runtime ebtables ethtool glusterfs-client htop init-system-helpers inotify-tools iotop iproute2 ipset iptables nftables jq libpam-pwquality libpwquality-tools mount nfs-common pigz socat sysfsutils sysstat util-linux xz-utils netcat-openbsd zip rng-tools kmod gcc make dkms initramfs-tools linux-headers-$(uname -r)) if [ "${OSVERSION}" = "26.04" ]; then - if isMinimalImage; then + if isMinimalImage || { [ "${IMG_SKU:-}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "cvm"; }; then # libc6-dev is needed for GPU driver installation at runtime and is not included on the 26.04 minimal base image pkg_list+=(libc6-dev) # cron/crontab is needed by init-aks-cloud.sh (RCV1P) since we create a ca-refresh cron job and is not included on the 26.04 minimal base image diff --git a/spec/vhdbuilder/packer/prune_server_cvm_spec.sh b/spec/vhdbuilder/packer/prune_server_cvm_spec.sh new file mode 100644 index 00000000000..38660b64011 --- /dev/null +++ b/spec/vhdbuilder/packer/prune_server_cvm_spec.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +Describe 'Ubuntu 26.04 server-cvm package pruning' + Include './vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh' + + setup() { + TEST_DIR="$(mktemp -d)" + OS_RELEASE_FILE="${TEST_DIR}/os-release" + CANDIDATE_PACKAGES_FILE="${TEST_DIR}/candidate-packages.txt" + REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" + printf 'ID=ubuntu\nVERSION_ID=\"26.04\"\n' > "${OS_RELEASE_FILE}" + printf 'candidate-one\ncandidate-two\n' > "${CANDIDATE_PACKAGES_FILE}" + printf 'required-one\nlinux-image-*-azure-fde\n' > "${REQUIRED_PACKAGES_FILE}" + IMG_SKU="server-cvm" + FEATURE_FLAGS="cvm" + } + + cleanup() { + rm -rf "${TEST_DIR}" + } + + BeforeEach 'setup' + AfterEach 'cleanup' + + It 'gates pruning to Ubuntu 26.04 server-cvm with the cvm feature' + When call should_prune_ubuntu_2604_server_cvm + The status should be success + End + + It 'does not prune a legacy CVM source' + IMG_SKU="22_04-lts-cvm" + When call should_prune_ubuntu_2604_server_cvm + The status should be failure + End + + It 'does not prune when cvm is only a substring of another feature' + FEATURE_FLAGS="notcvm" + When call should_prune_ubuntu_2604_server_cvm + The status should be failure + End + + It 'recognizes cvm as an exact item in a comma-separated feature list' + FEATURE_FLAGS="preview,cvm,other" + When call should_prune_ubuntu_2604_server_cvm + The status should be success + End + + It 'parses both apt removal record types' + Data + #|NOTE: This is only a simulation! + #|Remv candidate-one [1.0] + #|Purg candidate-two:amd64 [2.0] + #|Conf unrelated (3.0 Ubuntu:26.04/resolute [amd64]) + End + When call parse_simulated_removals + The output should eq "$(printf 'candidate-one\ncandidate-two:amd64')" + The status should be success + End + + It 'accepts a removal plan containing only candidates' + removal_file="${TEST_DIR}/removals.txt" + essential_file="${TEST_DIR}/essential.txt" + printf 'candidate-one\ncandidate-two:amd64\n' > "${removal_file}" + : > "${essential_file}" + When call validate_removal_plan "${removal_file}" "${essential_file}" + The status should be success + End + + It 'rejects a non-candidate removal' + removal_file="${TEST_DIR}/removals.txt" + essential_file="${TEST_DIR}/essential.txt" + printf 'candidate-one\nunrelated\n' > "${removal_file}" + : > "${essential_file}" + When run validate_removal_plan "${removal_file}" "${essential_file}" + The status should be failure + The stderr should include 'non-candidate package: unrelated' + End + + It 'rejects a required package matched by a version-independent pattern' + printf 'linux-image-7.0.0-1011-azure-fde\n' >> "${CANDIDATE_PACKAGES_FILE}" + removal_file="${TEST_DIR}/removals.txt" + essential_file="${TEST_DIR}/essential.txt" + printf 'linux-image-7.0.0-1011-azure-fde\n' > "${removal_file}" + : > "${essential_file}" + When run validate_removal_plan "${removal_file}" "${essential_file}" + The status should be failure + The stderr should include 'required CVM package' + End + + It 'rejects an Essential package' + removal_file="${TEST_DIR}/removals.txt" + essential_file="${TEST_DIR}/essential.txt" + printf 'candidate-one\n' > "${removal_file}" + printf 'candidate-one\n' > "${essential_file}" + When run validate_removal_plan "${removal_file}" "${essential_file}" + The status should be failure + The stderr should include 'Essential package' + End +End diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 7557baa1442..8d105a1184b 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -4,6 +4,17 @@ OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" +if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && [ "${IMG_SKU:-}" = "server-cvm" ]; then + case ",${FEATURE_FLAGS:-}," in + *,cvm,*) + echo "===== BASE IMAGE INSTALLED PACKAGES BEGIN =====" + dpkg-query -W -f='${binary:Package}\t${Version}\n' | sort + echo "===== BASE IMAGE INSTALLED PACKAGES END =====" + exit 1 + ;; + esac +fi + #the following sed removes all comments of the format {{/* */}} sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/provision_source.sh sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/tool_installs_distro.sh @@ -36,8 +47,9 @@ capture_benchmark "${SCRIPT_NAME}_source_packer_files_and_declare_variables" copyPackerFiles -# Install required dependencies needed to build minimal images if needed (currently only Ubuntu 26.04) -if isMinimalImage && isUbuntu "$OS"; then +# Install required dependencies needed to build pruned/minimal images if needed (currently only Ubuntu 26.04). +# server-cvm is pruned before this script runs, but intentionally does not carry the misleading minimal feature flag. +if isUbuntu "$OS" && { isMinimalImage || { [ "${OS_VERSION}" = "26.04" ] && [ "${IMG_SKU}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS}" | grep -Fxq "cvm"; }; }; then installMinimalBuildDeps fi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt new file mode 100644 index 00000000000..b73215f206b --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt @@ -0,0 +1,332 @@ +# Derived from server_fde_20260913.cvm-only-excluding-required.diff (2026-09-13). +# Versions and native :amd64 architecture qualifiers were removed. +apport-symptoms +appstream +bash-completion +bc +bcache-tools +bind9-dnsutils +bind9-host +bind9-libs +binutils +binutils-common +binutils-x86-64-linux-gnu +bolt +bpfcc-tools +bpftool +bpftrace +bsdextrautils +btrfs-progs +busybox-initramfs +busybox-static +cloud-initramfs-copymods +cloud-initramfs-dyn-netconf +command-not-found +cpio +cron +cron-daemon-common +debconf-i18n +dirmngr +dmeventd +dosfstools +dracut +dracut-core +dracut-install +dracut-network +eatmydata +ed +eject +exfatprogs +file +finalrd +fonts-ubuntu-console +friendly-recovery +ftp +fwupd +fwupd-signed +gawk +gdisk +gir1.2-packagekitglib-1.0 +git +git-man +gnupg +gnupg-l10n +gnupg-utils +gpg +gpg-agent +gpg-wks-client +gpgconf +gpgsm +groff-base +hdparm +htop +ibverbs-providers +ieee-data +inetutils-telnet +info +init +install-info +iptables +iputils-ping +iputils-tracepath +iso-codes +jq +kpartx +krb5-locales +landscape-common +less +libaio1t64 +libappstream5 +libassuan9 +libatasmart4 +libbinutils +libblockdev-crypto3 +libblockdev-fs3 +libblockdev-loop3 +libblockdev-mdraid3 +libblockdev-nvme3 +libblockdev-part3 +libblockdev-smart3 +libblockdev-swap3 +libblockdev-utils3 +libblockdev3 +libbpfcc +libbytesize-common +libbytesize1 +libclang-cpp21 +libclang1-21 +libctf-nobfd0 +libctf0 +libcurl3t64-gnutls +libdebuginfod-common +libdebuginfod1t64 +libdevmapper-event1.02.1 +libdrm-amdgpu1 +libduktape207 +libeatmydata1 +liberror-perl +libestr0 +libevdev2 +libevent-core-2.1-7t64 +libfastjson4 +libflashrom1 +libfribidi0 +libftdi1-2 +libfwupd3 +libfyaml0 +libgdbm-compat4t64 +libgdbm6t64 +libglib2.0-bin +libglib2.0-data +libgpg-error-l10n +libgpgme45 +libgpm2 +libgprofng0 +libgstreamer1.0-0 +libgudev-1.0-0 +libibverbs1 +libicu78 +libinih1 +libintl-perl +libintl-xs-perl +libip4tc2 +libip6tc2 +libisns0t64 +libjcat1 +libjemalloc2 +libjq1 +libjson-glib-1.0-0 +libjson-glib-1.0-common +libksba8 +liblastlog2-2 +libllvm21 +liblmdb0 +liblsof0 +liblvm2cmd2.03 +libmagic-mgc +libmagic1t64 +libmaxminddb0 +libmbim-glib4 +libmbim-proxy +libmbim-utils +libmm-glib0 +libmodule-find-perl +libmpathcmd0 +libmpathpersist0 +libmpfr6 +libmspack0t64 +libmultipath0 +libncurses6 +libnetfilter-conntrack3 +libnewt0.52 +libnfnetlink0 +libnl-route-3-200 +libnpth0t64 +libnspr4 +libnss-systemd +libnss3 +libntfs-3g89t64 +libnuma1 +libonig5 +libopeniscsiusr +libpackagekit-glib2-18 +libpam-cap +libpcap0.8t64 +libperl5.40 +libpipeline1 +libplymouth5 +libpolkit-agent-1-0 +libpolkit-gobject-1-0 +libproc-processtable-perl +libpython3.14 +libqmi-glib5 +libqmi-proxy +libqmi-utils +libqrtr-glib0 +libsasl2-modules +libsframe3 +libsgutils2-1.48 +libslang2 +libsodium23 +libsort-naturally-perl +libstemmer0d +libtcl8.6 +libterm-readkey-perl +libtext-iconv-perl +libtraceevent1 +libtraceevent1-plugin +libtracefs1 +libuchardet0 +libudisks2-0 +libunwind8 +liburcu8t64 +libuv1t64 +libvolume-key1 +libx11-6 +libx11-data +libxau6 +libxcb1 +libxdmcp6 +libxext6 +libxkbcommon0 +libxml2-16 +libxmlb2 +libxmlsec1-1 +libxmlsec1-openssl1 +libxmuu1 +libxslt1.1 +linux-perf +locales +logrotate +lshw +lsof +lvm2 +man-db +manpages +mdadm +modemmanager +motd-news-config +mtr-tiny +multipath-tools +nano +ncurses-term +needrestart +networkd-dispatcher +ntfs-3g +numactl +open-iscsi +open-vm-tools +overlayroot +packagekit +patch +perl +perl-modules-5.40 +pinentry-curses +plymouth +plymouth-theme-ubuntu-text +polkitd +powermgmt-base +psmisc +publicsuffix +python-babel-localedata +python3-automat +python3-babel +python3-boto3 +python3-botocore +python3-bpfcc +python3-commandnotfound +python3-constantly +python3-dateutil +python3-debian +python3-gdbm +python3-hamcrest +python3-hyperlink +python3-incremental +python3-jmespath +python3-linkify-it +python3-magic +python3-markdown-it +python3-mdurl +python3-netaddr +python3-netifaces +python3-openssl +python3-packaging +python3-pexpect +python3-ptyprocess +python3-pyasn1 +python3-pyasn1-modules +python3-pygments +python3-rich +python3-s3transfer +python3-service-identity +python3-six +python3-software-properties +python3-systemd +python3-twisted +python3-uc-micro +python3-zope.interface +python3.14-gdbm +rsync +rsyslog +sg3-utils +sg3-utils-udev +sgml-base +shared-mime-info +software-properties-common +sos +strace +systemd-cryptsetup +systemd-hwe-hwdb +tcl +tcl8.6 +tcpdump +telnet +thin-provisioning-tools +time +tmux +tnftp +trace-cmd +ubuntu-kernel-accessories +ubuntu-minimal +ubuntu-pro-client-l10n +ubuntu-server +ubuntu-standard +udisks2 +ufw +update-manager-core +update-notifier-common +usb-modeswitch +usb-modeswitch-data +util-linux-extra +vim +vim-common +vim-runtime +vim-tiny +whiptail +xauth +xdg-user-dirs +xfsprogs +xml-core +xxd +zerofree +zstd diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh b/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh new file mode 100644 index 00000000000..b9d6ae609fe --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh @@ -0,0 +1,266 @@ +#!/bin/bash +# shellcheck disable=SC3010 # This script is invoked explicitly with bash by the CVM Packer template. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CANDIDATE_PACKAGES_FILE="${CANDIDATE_PACKAGES_FILE:-${SCRIPT_DIR}/candidate-packages.txt}" +REQUIRED_PACKAGES_FILE="${REQUIRED_PACKAGES_FILE:-${SCRIPT_DIR}/required-packages.txt}" +OS_RELEASE_FILE="${OS_RELEASE_FILE:-/etc/os-release}" + +feature_flag_enabled() { + local expected_flag="$1" + tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "${expected_flag}" +} + +should_prune_ubuntu_2604_server_cvm() { + local os_id os_version + + [ -r "${OS_RELEASE_FILE}" ] || return 1 + # shellcheck disable=SC1090 + . "${OS_RELEASE_FILE}" + os_id="${ID:-}" + os_version="${VERSION_ID:-}" + + [ "${os_id,,}" = "ubuntu" ] && + [ "${os_version}" = "26.04" ] && + [ "${IMG_SKU:-}" = "server-cvm" ] && + feature_flag_enabled "cvm" +} + +read_package_list() { + local package_file="$1" + sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "${package_file}" +} + +validate_package_lists() { + local candidate package required + + [ -s "${CANDIDATE_PACKAGES_FILE}" ] || { + echo "Candidate package list is missing or empty: ${CANDIDATE_PACKAGES_FILE}" >&2 + return 1 + } + [ -s "${REQUIRED_PACKAGES_FILE}" ] || { + echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 + return 1 + } + + while IFS= read -r candidate; do + if [[ ! "${candidate}" =~ ^[a-z0-9][a-z0-9+.-]*$ ]]; then + echo "Invalid candidate package name: ${candidate}" >&2 + return 1 + fi + done < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") + + while IFS= read -r required; do + if [[ ! "${required}" =~ ^[a-z0-9][a-z0-9+.*-]*$ ]]; then + echo "Invalid required package pattern: ${required}" >&2 + return 1 + fi + while IFS= read -r candidate; do + # shellcheck disable=SC2053 # required entries intentionally support package-name globs. + if [[ "${candidate}" == ${required} ]]; then + echo "Candidate package is protected by required pattern ${required}: ${candidate}" >&2 + return 1 + fi + done < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") + done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") +} + +normalize_package_name() { + printf '%s\n' "${1%%:*}" +} + +package_is_candidate() { + local package + package="$(normalize_package_name "$1")" + grep -Fxq "${package}" < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") +} + +package_is_required() { + local package required + package="$(normalize_package_name "$1")" + while IFS= read -r required; do + # shellcheck disable=SC2053 # required entries intentionally support package-name globs. + if [[ "${package}" == ${required} ]]; then + return 0 + fi + done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") + return 1 +} + +list_installed_packages() { + dpkg-query -W -f='${binary:Package}\t${db:Status-Status}\n' | + while IFS=$'\t' read -r package status; do + [ "${status}" = "installed" ] && printf '%s\n' "${package}" + done +} + +verify_required_packages_installed() { + local installed_file="$1" + local required package matched + + while IFS= read -r required; do + matched=false + while IFS= read -r package; do + package="$(normalize_package_name "${package}")" + # shellcheck disable=SC2053 # required entries intentionally support package-name globs. + if [[ "${package}" == ${required} ]]; then + matched=true + break + fi + done < "${installed_file}" + if [ "${matched}" != "true" ]; then + echo "Required CVM package pattern is not installed: ${required}" >&2 + return 1 + fi + done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") +} + +parse_simulated_removals() { + local action package + while read -r action package _; do + case "${action}" in + Remv | Purg) + [ -n "${package}" ] && printf '%s\n' "${package}" + ;; + esac + done +} + +validate_removal_plan() { + local removal_file="$1" + local essential_file="$2" + local package normalized + + [ -s "${removal_file}" ] || { + echo "apt simulation did not report any package removals" >&2 + return 1 + } + + while IFS= read -r package; do + normalized="$(normalize_package_name "${package}")" + if ! package_is_candidate "${normalized}"; then + echo "apt simulation would remove non-candidate package: ${package}" >&2 + return 1 + fi + if package_is_required "${normalized}"; then + echo "apt simulation would remove required CVM package: ${package}" >&2 + return 1 + fi + if grep -Fxq "${normalized}" "${essential_file}"; then + echo "apt simulation would remove Essential package: ${package}" >&2 + return 1 + fi + done < "${removal_file}" +} + +verify_all_candidates_planned() { + local installed_candidates_file="$1" + local removal_file="$2" + local package normalized + + while IFS= read -r package; do + normalized="$(normalize_package_name "${package}")" + if ! grep -Fxq "${normalized}" < <(sed 's/:.*//' "${removal_file}"); then + echo "apt simulation omitted installed candidate package: ${package}" >&2 + return 1 + fi + done < "${installed_candidates_file}" +} + +verify_no_candidates_installed() { + local installed_file="$1" + local package + + while IFS= read -r package; do + if package_is_candidate "${package}"; then + echo "Candidate package remained installed after pruning: ${package}" >&2 + return 1 + fi + done < "${installed_file}" +} + +main() { + local work_dir installed_before installed_after installed_candidates + local manual_packages essential_packages simulation_output removal_plan audit_output + local package normalized + local -a purge_packages=() + + if ! should_prune_ubuntu_2604_server_cvm; then + echo "Skipping package pruning: only Ubuntu 26.04 server-cvm builds with the cvm feature are supported" + return 0 + fi + + validate_package_lists + + work_dir="$(mktemp -d)" + PRUNE_WORK_DIR="${work_dir}" + trap 'rm -rf "${PRUNE_WORK_DIR}"' EXIT + installed_before="${work_dir}/installed-before.txt" + installed_after="${work_dir}/installed-after.txt" + installed_candidates="${work_dir}/installed-candidates.txt" + manual_packages="${work_dir}/manual-packages.txt" + essential_packages="${work_dir}/essential-packages.txt" + simulation_output="${work_dir}/apt-simulation.txt" + removal_plan="${work_dir}/removal-plan.txt" + audit_output="${work_dir}/dpkg-audit.txt" + + list_installed_packages | sort -u > "${installed_before}" + verify_required_packages_installed "${installed_before}" + + while IFS= read -r package; do + normalized="$(normalize_package_name "${package}")" + if package_is_candidate "${normalized}"; then + printf '%s\n' "${package}" >> "${installed_candidates}" + else + printf '%s\n' "${package}" >> "${manual_packages}" + fi + if [ "$(dpkg-query -W -f='${Essential}' "${package}")" = "yes" ]; then + printf '%s\n' "${normalized}" >> "${essential_packages}" + fi + done < "${installed_before}" + + if [ -s "${manual_packages}" ]; then + xargs -r -n 100 apt-mark -o DPkg::Lock::Timeout=300 manual < "${manual_packages}" + fi + + if [ ! -s "${installed_candidates}" ]; then + echo "No installed server-cvm pruning candidates were found" + dpkg --audit > "${audit_output}" + [ ! -s "${audit_output}" ] || { + cat "${audit_output}" >&2 + return 1 + } + apt-get -o DPkg::Lock::Timeout=300 check + return 0 + fi + + mapfile -t purge_packages < "${installed_candidates}" + if ! DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 --simulate purge "${purge_packages[@]}" > "${simulation_output}" 2>&1; then + cat "${simulation_output}" >&2 + echo "apt purge simulation failed" >&2 + return 1 + fi + + parse_simulated_removals < "${simulation_output}" | sort -u > "${removal_plan}" + validate_removal_plan "${removal_plan}" "${essential_packages}" + verify_all_candidates_planned "${installed_candidates}" "${removal_plan}" + + echo "Purging $(wc -l < "${installed_candidates}") installed server-cvm candidate packages" + DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove "${purge_packages[@]}" + + list_installed_packages | sort -u > "${installed_after}" + verify_required_packages_installed "${installed_after}" + verify_no_candidates_installed "${installed_after}" + + dpkg --audit > "${audit_output}" + if [ -s "${audit_output}" ]; then + cat "${audit_output}" >&2 + return 1 + fi + apt-get -o DPkg::Lock::Timeout=300 check +} + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + set -euo pipefail + main "$@" +fi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt new file mode 100644 index 00000000000..ce095cd24a5 --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -0,0 +1,23 @@ +# Derived from server_fde_20260913.cvm-required.diff (2026-09-13). +# Versions/native architecture qualifiers were removed; * covers kernel ABI package names. +cryptsetup +cryptsetup-bin +libcryptsetup12 +lsscsi +linux-azure-fde +linux-azure-fde-cloud-tools-* +linux-azure-fde-headers-* +linux-azure-fde-tools-* +linux-azure-tools-* +linux-cloud-tools-*-azure-fde +linux-cloud-tools-azure-fde +linux-headers-*-azure-fde +linux-headers-azure-fde +linux-image-*-azure-fde +linux-image-azure-fde +linux-main-modules-zfs-*-azure-fde +linux-modules-*-azure-fde +linux-tools-*-azure +linux-tools-*-azure-fde +linux-tools-azure-fde +nullboot diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index f13b5ff46a0..80e9d5262b0 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -359,6 +359,21 @@ "source": "vhdbuilder/packer/pre-install-dependencies.sh", "destination": "/home/packer/pre-install-dependencies.sh" }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh", + "destination": "/home/packer/prune-server-cvm.sh" + }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt", + "destination": "/home/packer/candidate-packages.txt" + }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", + "destination": "/home/packer/required-packages.txt" + }, { "type": "file", "source": "vhdbuilder/packer/install-ig.sh", From 90558a6876ac1e7a720b2a0715621adc2c4ae8ff Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 17:05:02 -0400 Subject: [PATCH 08/47] fix --- .pipelines/templates/.builder-release-template.yaml | 1 + vhdbuilder/packer/pre-install-dependencies.sh | 11 ----------- vhdbuilder/packer/vhd-image-builder-cvm.json | 6 ++++++ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 24a9b3f60cd..146706869e2 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -98,6 +98,7 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 8d105a1184b..965c8776ecf 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -4,17 +4,6 @@ OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" -if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && [ "${IMG_SKU:-}" = "server-cvm" ]; then - case ",${FEATURE_FLAGS:-}," in - *,cvm,*) - echo "===== BASE IMAGE INSTALLED PACKAGES BEGIN =====" - dpkg-query -W -f='${binary:Package}\t${Version}\n' | sort - echo "===== BASE IMAGE INSTALLED PACKAGES END =====" - exit 1 - ;; - esac -fi - #the following sed removes all comments of the format {{/* */}} sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/provision_source.sh sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/tool_installs_distro.sh diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 80e9d5262b0..44e64059e38 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -374,6 +374,12 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", "destination": "/home/packer/required-packages.txt" }, + { + "type": "shell", + "inline": [ + "sudo FEATURE_FLAGS={{user `feature_flags`}} IMG_SKU={{user `img_sku`}} /bin/bash -eux /home/packer/prune-server-cvm.sh" + ] + }, { "type": "file", "source": "vhdbuilder/packer/install-ig.sh", From 6a84560aace8ff33269dfadc78b4709cf4cb4e14 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Mon, 14 Sep 2026 17:14:34 -0400 Subject: [PATCH 09/47] fix --- .pipelines/templates/.builder-release-template.yaml | 1 - vhdbuilder/packer/pre-install-dependencies.sh | 7 +++++++ vhdbuilder/packer/vhd-image-builder-cvm.json | 6 ------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 146706869e2..24a9b3f60cd 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -98,7 +98,6 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 965c8776ecf..94aec8ecea3 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -4,6 +4,13 @@ OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" +if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ]; then + echo "===== BASE IMAGE INSTALLED PACKAGES BEGIN =====" + dpkg-query -W -f='${binary:Package}\t${Version}\n' | sort + echo "===== BASE IMAGE INSTALLED PACKAGES END =====" + exit 1 +fi + #the following sed removes all comments of the format {{/* */}} sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/provision_source.sh sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/tool_installs_distro.sh diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 44e64059e38..80e9d5262b0 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -374,12 +374,6 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", "destination": "/home/packer/required-packages.txt" }, - { - "type": "shell", - "inline": [ - "sudo FEATURE_FLAGS={{user `feature_flags`}} IMG_SKU={{user `img_sku`}} /bin/bash -eux /home/packer/prune-server-cvm.sh" - ] - }, { "type": "file", "source": "vhdbuilder/packer/install-ig.sh", From 67c56cf8e9f04010d73ff7c4e226eb7359b9a066 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 10:45:57 -0400 Subject: [PATCH 10/47] fix --- .../templates/.builder-release-template.yaml | 1 + .../packer/prune_server_cvm_spec.sh | 46 ++++++++--- vhdbuilder/packer/pre-install-dependencies.sh | 16 ++-- ...es.txt => marked-for-removal-packages.txt} | 3 - .../ubuntu-2604-cvm/prune-server-cvm.sh | 80 +++++++++---------- .../ubuntu-2604-cvm/required-packages.txt | 2 - vhdbuilder/packer/vhd-image-builder-cvm.json | 4 +- 7 files changed, 84 insertions(+), 68 deletions(-) rename vhdbuilder/packer/ubuntu-2604-cvm/{candidate-packages.txt => marked-for-removal-packages.txt} (96%) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 24a9b3f60cd..146706869e2 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -98,6 +98,7 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) diff --git a/spec/vhdbuilder/packer/prune_server_cvm_spec.sh b/spec/vhdbuilder/packer/prune_server_cvm_spec.sh index 38660b64011..ff137c633d7 100644 --- a/spec/vhdbuilder/packer/prune_server_cvm_spec.sh +++ b/spec/vhdbuilder/packer/prune_server_cvm_spec.sh @@ -6,10 +6,10 @@ Describe 'Ubuntu 26.04 server-cvm package pruning' setup() { TEST_DIR="$(mktemp -d)" OS_RELEASE_FILE="${TEST_DIR}/os-release" - CANDIDATE_PACKAGES_FILE="${TEST_DIR}/candidate-packages.txt" + MARKED_FOR_REMOVAL_PACKAGES_FILE="${TEST_DIR}/marked-for-removal-packages.txt" REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" printf 'ID=ubuntu\nVERSION_ID=\"26.04\"\n' > "${OS_RELEASE_FILE}" - printf 'candidate-one\ncandidate-two\n' > "${CANDIDATE_PACKAGES_FILE}" + printf 'marked-one\nmarked-two\n' > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" printf 'required-one\nlinux-image-*-azure-fde\n' > "${REQUIRED_PACKAGES_FILE}" IMG_SKU="server-cvm" FEATURE_FLAGS="cvm" @@ -33,6 +33,26 @@ Describe 'Ubuntu 26.04 server-cvm package pruning' The status should be failure End + It 'does not prune Ubuntu 26.04 Minimal AMD64' + IMG_SKU="minimal" + FEATURE_FLAGS="minimal" + When call should_prune_ubuntu_2604_server_cvm + The status should be failure + End + + It 'does not prune Ubuntu 26.04 Minimal ARM64' + IMG_SKU="minimal-arm64" + FEATURE_FLAGS="minimal" + When call should_prune_ubuntu_2604_server_cvm + The status should be failure + End + + It 'does not prune older server-cvm images' + printf 'ID=ubuntu\nVERSION_ID="24.04"\n' > "${OS_RELEASE_FILE}" + When call should_prune_ubuntu_2604_server_cvm + The status should be failure + End + It 'does not prune when cvm is only a substring of another feature' FEATURE_FLAGS="notcvm" When call should_prune_ubuntu_2604_server_cvm @@ -48,36 +68,36 @@ Describe 'Ubuntu 26.04 server-cvm package pruning' It 'parses both apt removal record types' Data #|NOTE: This is only a simulation! - #|Remv candidate-one [1.0] - #|Purg candidate-two:amd64 [2.0] + #|Remv marked-one [1.0] + #|Purg marked-two:amd64 [2.0] #|Conf unrelated (3.0 Ubuntu:26.04/resolute [amd64]) End When call parse_simulated_removals - The output should eq "$(printf 'candidate-one\ncandidate-two:amd64')" + The output should eq "$(printf 'marked-one\nmarked-two:amd64')" The status should be success End - It 'accepts a removal plan containing only candidates' + It 'accepts a removal plan containing only packages marked for removal' removal_file="${TEST_DIR}/removals.txt" essential_file="${TEST_DIR}/essential.txt" - printf 'candidate-one\ncandidate-two:amd64\n' > "${removal_file}" + printf 'marked-one\nmarked-two:amd64\n' > "${removal_file}" : > "${essential_file}" When call validate_removal_plan "${removal_file}" "${essential_file}" The status should be success End - It 'rejects a non-candidate removal' + It 'rejects an unspecified removal' removal_file="${TEST_DIR}/removals.txt" essential_file="${TEST_DIR}/essential.txt" - printf 'candidate-one\nunrelated\n' > "${removal_file}" + printf 'marked-one\nunrelated\n' > "${removal_file}" : > "${essential_file}" When run validate_removal_plan "${removal_file}" "${essential_file}" The status should be failure - The stderr should include 'non-candidate package: unrelated' + The stderr should include 'unspecified package: unrelated' End It 'rejects a required package matched by a version-independent pattern' - printf 'linux-image-7.0.0-1011-azure-fde\n' >> "${CANDIDATE_PACKAGES_FILE}" + printf 'linux-image-7.0.0-1011-azure-fde\n' >> "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" removal_file="${TEST_DIR}/removals.txt" essential_file="${TEST_DIR}/essential.txt" printf 'linux-image-7.0.0-1011-azure-fde\n' > "${removal_file}" @@ -90,8 +110,8 @@ Describe 'Ubuntu 26.04 server-cvm package pruning' It 'rejects an Essential package' removal_file="${TEST_DIR}/removals.txt" essential_file="${TEST_DIR}/essential.txt" - printf 'candidate-one\n' > "${removal_file}" - printf 'candidate-one\n' > "${essential_file}" + printf 'marked-one\n' > "${removal_file}" + printf 'marked-one\n' > "${essential_file}" When run validate_removal_plan "${removal_file}" "${essential_file}" The status should be failure The stderr should include 'Essential package' diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 94aec8ecea3..e3b074433e9 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -2,15 +2,16 @@ OS=$(sort -r /etc/*-release | sed -n 's/^ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') -THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" -if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ]; then - echo "===== BASE IMAGE INSTALLED PACKAGES BEGIN =====" - dpkg-query -W -f='${binary:Package}\t${Version}\n' | sort - echo "===== BASE IMAGE INSTALLED PACKAGES END =====" - exit 1 +if [ "$OS" = "UBUNTU" ] && + [ "$OS_VERSION" = "26.04" ] && + [ "${IMG_SKU:-}" = "server-cvm" ] && + tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "cvm"; then + /bin/bash /home/packer/prune-server-cvm.sh fi +THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" + #the following sed removes all comments of the format {{/* */}} sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/provision_source.sh sed -i 's/{{\/\*[^*]*\*\/}}//g' /home/packer/tool_installs_distro.sh @@ -43,8 +44,7 @@ capture_benchmark "${SCRIPT_NAME}_source_packer_files_and_declare_variables" copyPackerFiles -# Install required dependencies needed to build pruned/minimal images if needed (currently only Ubuntu 26.04). -# server-cvm is pruned before this script runs, but intentionally does not carry the misleading minimal feature flag. +# Install required dependencies needed to build minimal images if needed (currently only Ubuntu 26.04) if isUbuntu "$OS" && { isMinimalImage || { [ "${OS_VERSION}" = "26.04" ] && [ "${IMG_SKU}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS}" | grep -Fxq "cvm"; }; }; then installMinimalBuildDeps fi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt similarity index 96% rename from vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt rename to vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index b73215f206b..f87b65e6046 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -1,5 +1,3 @@ -# Derived from server_fde_20260913.cvm-only-excluding-required.diff (2026-09-13). -# Versions and native :amd64 architecture qualifiers were removed. apport-symptoms appstream bash-completion @@ -327,6 +325,5 @@ xauth xdg-user-dirs xfsprogs xml-core -xxd zerofree zstd diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh b/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh index b9d6ae609fe..ecf4cbe4f02 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC3010 # This script is invoked explicitly with bash by the CVM Packer template. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CANDIDATE_PACKAGES_FILE="${CANDIDATE_PACKAGES_FILE:-${SCRIPT_DIR}/candidate-packages.txt}" +MARKED_FOR_REMOVAL_PACKAGES_FILE="${MARKED_FOR_REMOVAL_PACKAGES_FILE:-${SCRIPT_DIR}/marked-for-removal-packages.txt}" REQUIRED_PACKAGES_FILE="${REQUIRED_PACKAGES_FILE:-${SCRIPT_DIR}/required-packages.txt}" OS_RELEASE_FILE="${OS_RELEASE_FILE:-/etc/os-release}" @@ -32,10 +32,10 @@ read_package_list() { } validate_package_lists() { - local candidate package required + local marked_for_removal package required - [ -s "${CANDIDATE_PACKAGES_FILE}" ] || { - echo "Candidate package list is missing or empty: ${CANDIDATE_PACKAGES_FILE}" >&2 + [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { + echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 return 1 } [ -s "${REQUIRED_PACKAGES_FILE}" ] || { @@ -43,25 +43,25 @@ validate_package_lists() { return 1 } - while IFS= read -r candidate; do - if [[ ! "${candidate}" =~ ^[a-z0-9][a-z0-9+.-]*$ ]]; then - echo "Invalid candidate package name: ${candidate}" >&2 + while IFS= read -r marked_for_removal; do + if [[ ! "${marked_for_removal}" =~ ^[a-z0-9][a-z0-9+.-]*$ ]]; then + echo "Invalid marked-for-removal package name: ${marked_for_removal}" >&2 return 1 fi - done < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") + done < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") while IFS= read -r required; do if [[ ! "${required}" =~ ^[a-z0-9][a-z0-9+.*-]*$ ]]; then echo "Invalid required package pattern: ${required}" >&2 return 1 fi - while IFS= read -r candidate; do + while IFS= read -r marked_for_removal; do # shellcheck disable=SC2053 # required entries intentionally support package-name globs. - if [[ "${candidate}" == ${required} ]]; then - echo "Candidate package is protected by required pattern ${required}: ${candidate}" >&2 + if [[ "${marked_for_removal}" == ${required} ]]; then + echo "Marked-for-removal package is protected by required pattern ${required}: ${marked_for_removal}" >&2 return 1 fi - done < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") + done < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") } @@ -69,10 +69,10 @@ normalize_package_name() { printf '%s\n' "${1%%:*}" } -package_is_candidate() { +package_is_marked_for_removal() { local package package="$(normalize_package_name "$1")" - grep -Fxq "${package}" < <(read_package_list "${CANDIDATE_PACKAGES_FILE}") + grep -Fxq "${package}" < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") } package_is_required() { @@ -138,14 +138,14 @@ validate_removal_plan() { while IFS= read -r package; do normalized="$(normalize_package_name "${package}")" - if ! package_is_candidate "${normalized}"; then - echo "apt simulation would remove non-candidate package: ${package}" >&2 - return 1 - fi if package_is_required "${normalized}"; then echo "apt simulation would remove required CVM package: ${package}" >&2 return 1 fi + if ! package_is_marked_for_removal "${normalized}"; then + echo "apt simulation would remove unspecified package: ${package}" >&2 + return 1 + fi if grep -Fxq "${normalized}" "${essential_file}"; then echo "apt simulation would remove Essential package: ${package}" >&2 return 1 @@ -153,35 +153,35 @@ validate_removal_plan() { done < "${removal_file}" } -verify_all_candidates_planned() { - local installed_candidates_file="$1" +verify_all_marked_for_removal_planned() { + local installed_marked_for_removal_file="$1" local removal_file="$2" local package normalized while IFS= read -r package; do normalized="$(normalize_package_name "${package}")" if ! grep -Fxq "${normalized}" < <(sed 's/:.*//' "${removal_file}"); then - echo "apt simulation omitted installed candidate package: ${package}" >&2 + echo "apt simulation omitted installed marked-for-removal package: ${package}" >&2 return 1 fi - done < "${installed_candidates_file}" + done < "${installed_marked_for_removal_file}" } -verify_no_candidates_installed() { +verify_no_marked_for_removal_installed() { local installed_file="$1" local package while IFS= read -r package; do - if package_is_candidate "${package}"; then - echo "Candidate package remained installed after pruning: ${package}" >&2 + if package_is_marked_for_removal "${package}"; then + echo "Marked-for-removal package remained installed after pruning: ${package}" >&2 return 1 fi done < "${installed_file}" } main() { - local work_dir installed_before installed_after installed_candidates - local manual_packages essential_packages simulation_output removal_plan audit_output + local work_dir installed_before installed_after installed_marked_for_removal + local unspecified_packages essential_packages simulation_output removal_plan audit_output local package normalized local -a purge_packages=() @@ -197,8 +197,8 @@ main() { trap 'rm -rf "${PRUNE_WORK_DIR}"' EXIT installed_before="${work_dir}/installed-before.txt" installed_after="${work_dir}/installed-after.txt" - installed_candidates="${work_dir}/installed-candidates.txt" - manual_packages="${work_dir}/manual-packages.txt" + installed_marked_for_removal="${work_dir}/installed-marked-for-removal.txt" + unspecified_packages="${work_dir}/unspecified-packages.txt" essential_packages="${work_dir}/essential-packages.txt" simulation_output="${work_dir}/apt-simulation.txt" removal_plan="${work_dir}/removal-plan.txt" @@ -209,22 +209,22 @@ main() { while IFS= read -r package; do normalized="$(normalize_package_name "${package}")" - if package_is_candidate "${normalized}"; then - printf '%s\n' "${package}" >> "${installed_candidates}" + if package_is_marked_for_removal "${normalized}"; then + printf '%s\n' "${package}" >> "${installed_marked_for_removal}" else - printf '%s\n' "${package}" >> "${manual_packages}" + printf '%s\n' "${package}" >> "${unspecified_packages}" fi if [ "$(dpkg-query -W -f='${Essential}' "${package}")" = "yes" ]; then printf '%s\n' "${normalized}" >> "${essential_packages}" fi done < "${installed_before}" - if [ -s "${manual_packages}" ]; then - xargs -r -n 100 apt-mark -o DPkg::Lock::Timeout=300 manual < "${manual_packages}" + if [ -s "${unspecified_packages}" ]; then + xargs -r -n 100 apt-mark -o DPkg::Lock::Timeout=300 manual < "${unspecified_packages}" fi - if [ ! -s "${installed_candidates}" ]; then - echo "No installed server-cvm pruning candidates were found" + if [ ! -s "${installed_marked_for_removal}" ]; then + echo "No installed server-cvm packages marked for removal were found" dpkg --audit > "${audit_output}" [ ! -s "${audit_output}" ] || { cat "${audit_output}" >&2 @@ -234,7 +234,7 @@ main() { return 0 fi - mapfile -t purge_packages < "${installed_candidates}" + mapfile -t purge_packages < "${installed_marked_for_removal}" if ! DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 --simulate purge "${purge_packages[@]}" > "${simulation_output}" 2>&1; then cat "${simulation_output}" >&2 echo "apt purge simulation failed" >&2 @@ -243,14 +243,14 @@ main() { parse_simulated_removals < "${simulation_output}" | sort -u > "${removal_plan}" validate_removal_plan "${removal_plan}" "${essential_packages}" - verify_all_candidates_planned "${installed_candidates}" "${removal_plan}" + verify_all_marked_for_removal_planned "${installed_marked_for_removal}" "${removal_plan}" - echo "Purging $(wc -l < "${installed_candidates}") installed server-cvm candidate packages" + echo "Purging $(wc -l < "${installed_marked_for_removal}") installed server-cvm packages marked for removal" DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove "${purge_packages[@]}" list_installed_packages | sort -u > "${installed_after}" verify_required_packages_installed "${installed_after}" - verify_no_candidates_installed "${installed_after}" + verify_no_marked_for_removal_installed "${installed_after}" dpkg --audit > "${audit_output}" if [ -s "${audit_output}" ]; then diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index ce095cd24a5..f50be581bac 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -1,5 +1,3 @@ -# Derived from server_fde_20260913.cvm-required.diff (2026-09-13). -# Versions/native architecture qualifiers were removed; * covers kernel ABI package names. cryptsetup cryptsetup-bin libcryptsetup12 diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 80e9d5262b0..a69074dd051 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -366,8 +366,8 @@ }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/candidate-packages.txt", - "destination": "/home/packer/candidate-packages.txt" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt", + "destination": "/home/packer/marked-for-removal-packages.txt" }, { "type": "file", From 9a298a4b1a15f41148532a6049fa6f6b30c88adf Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 11:06:30 -0400 Subject: [PATCH 11/47] fix --- .../packer/prune_server_cvm_spec.sh | 119 -------- vhdbuilder/packer/pre-install-dependencies.sh | 2 +- .../ubuntu-2604-cvm/prune-server-cvm.sh | 266 ------------------ .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 52 ++++ vhdbuilder/packer/vhd-image-builder-cvm.json | 4 +- 5 files changed, 55 insertions(+), 388 deletions(-) delete mode 100644 spec/vhdbuilder/packer/prune_server_cvm_spec.sh delete mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh diff --git a/spec/vhdbuilder/packer/prune_server_cvm_spec.sh b/spec/vhdbuilder/packer/prune_server_cvm_spec.sh deleted file mode 100644 index ff137c633d7..00000000000 --- a/spec/vhdbuilder/packer/prune_server_cvm_spec.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash - -Describe 'Ubuntu 26.04 server-cvm package pruning' - Include './vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh' - - setup() { - TEST_DIR="$(mktemp -d)" - OS_RELEASE_FILE="${TEST_DIR}/os-release" - MARKED_FOR_REMOVAL_PACKAGES_FILE="${TEST_DIR}/marked-for-removal-packages.txt" - REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" - printf 'ID=ubuntu\nVERSION_ID=\"26.04\"\n' > "${OS_RELEASE_FILE}" - printf 'marked-one\nmarked-two\n' > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" - printf 'required-one\nlinux-image-*-azure-fde\n' > "${REQUIRED_PACKAGES_FILE}" - IMG_SKU="server-cvm" - FEATURE_FLAGS="cvm" - } - - cleanup() { - rm -rf "${TEST_DIR}" - } - - BeforeEach 'setup' - AfterEach 'cleanup' - - It 'gates pruning to Ubuntu 26.04 server-cvm with the cvm feature' - When call should_prune_ubuntu_2604_server_cvm - The status should be success - End - - It 'does not prune a legacy CVM source' - IMG_SKU="22_04-lts-cvm" - When call should_prune_ubuntu_2604_server_cvm - The status should be failure - End - - It 'does not prune Ubuntu 26.04 Minimal AMD64' - IMG_SKU="minimal" - FEATURE_FLAGS="minimal" - When call should_prune_ubuntu_2604_server_cvm - The status should be failure - End - - It 'does not prune Ubuntu 26.04 Minimal ARM64' - IMG_SKU="minimal-arm64" - FEATURE_FLAGS="minimal" - When call should_prune_ubuntu_2604_server_cvm - The status should be failure - End - - It 'does not prune older server-cvm images' - printf 'ID=ubuntu\nVERSION_ID="24.04"\n' > "${OS_RELEASE_FILE}" - When call should_prune_ubuntu_2604_server_cvm - The status should be failure - End - - It 'does not prune when cvm is only a substring of another feature' - FEATURE_FLAGS="notcvm" - When call should_prune_ubuntu_2604_server_cvm - The status should be failure - End - - It 'recognizes cvm as an exact item in a comma-separated feature list' - FEATURE_FLAGS="preview,cvm,other" - When call should_prune_ubuntu_2604_server_cvm - The status should be success - End - - It 'parses both apt removal record types' - Data - #|NOTE: This is only a simulation! - #|Remv marked-one [1.0] - #|Purg marked-two:amd64 [2.0] - #|Conf unrelated (3.0 Ubuntu:26.04/resolute [amd64]) - End - When call parse_simulated_removals - The output should eq "$(printf 'marked-one\nmarked-two:amd64')" - The status should be success - End - - It 'accepts a removal plan containing only packages marked for removal' - removal_file="${TEST_DIR}/removals.txt" - essential_file="${TEST_DIR}/essential.txt" - printf 'marked-one\nmarked-two:amd64\n' > "${removal_file}" - : > "${essential_file}" - When call validate_removal_plan "${removal_file}" "${essential_file}" - The status should be success - End - - It 'rejects an unspecified removal' - removal_file="${TEST_DIR}/removals.txt" - essential_file="${TEST_DIR}/essential.txt" - printf 'marked-one\nunrelated\n' > "${removal_file}" - : > "${essential_file}" - When run validate_removal_plan "${removal_file}" "${essential_file}" - The status should be failure - The stderr should include 'unspecified package: unrelated' - End - - It 'rejects a required package matched by a version-independent pattern' - printf 'linux-image-7.0.0-1011-azure-fde\n' >> "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" - removal_file="${TEST_DIR}/removals.txt" - essential_file="${TEST_DIR}/essential.txt" - printf 'linux-image-7.0.0-1011-azure-fde\n' > "${removal_file}" - : > "${essential_file}" - When run validate_removal_plan "${removal_file}" "${essential_file}" - The status should be failure - The stderr should include 'required CVM package' - End - - It 'rejects an Essential package' - removal_file="${TEST_DIR}/removals.txt" - essential_file="${TEST_DIR}/essential.txt" - printf 'marked-one\n' > "${removal_file}" - printf 'marked-one\n' > "${essential_file}" - When run validate_removal_plan "${removal_file}" "${essential_file}" - The status should be failure - The stderr should include 'Essential package' - End -End diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index e3b074433e9..05c13ece6fa 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -7,7 +7,7 @@ if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && [ "${IMG_SKU:-}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "cvm"; then - /bin/bash /home/packer/prune-server-cvm.sh + /bin/bash /home/packer/trim-2604-cvm-packages.sh fi THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh b/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh deleted file mode 100644 index ecf4cbe4f02..00000000000 --- a/vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh +++ /dev/null @@ -1,266 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC3010 # This script is invoked explicitly with bash by the CVM Packer template. - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -MARKED_FOR_REMOVAL_PACKAGES_FILE="${MARKED_FOR_REMOVAL_PACKAGES_FILE:-${SCRIPT_DIR}/marked-for-removal-packages.txt}" -REQUIRED_PACKAGES_FILE="${REQUIRED_PACKAGES_FILE:-${SCRIPT_DIR}/required-packages.txt}" -OS_RELEASE_FILE="${OS_RELEASE_FILE:-/etc/os-release}" - -feature_flag_enabled() { - local expected_flag="$1" - tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "${expected_flag}" -} - -should_prune_ubuntu_2604_server_cvm() { - local os_id os_version - - [ -r "${OS_RELEASE_FILE}" ] || return 1 - # shellcheck disable=SC1090 - . "${OS_RELEASE_FILE}" - os_id="${ID:-}" - os_version="${VERSION_ID:-}" - - [ "${os_id,,}" = "ubuntu" ] && - [ "${os_version}" = "26.04" ] && - [ "${IMG_SKU:-}" = "server-cvm" ] && - feature_flag_enabled "cvm" -} - -read_package_list() { - local package_file="$1" - sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "${package_file}" -} - -validate_package_lists() { - local marked_for_removal package required - - [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { - echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 - return 1 - } - [ -s "${REQUIRED_PACKAGES_FILE}" ] || { - echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 - return 1 - } - - while IFS= read -r marked_for_removal; do - if [[ ! "${marked_for_removal}" =~ ^[a-z0-9][a-z0-9+.-]*$ ]]; then - echo "Invalid marked-for-removal package name: ${marked_for_removal}" >&2 - return 1 - fi - done < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") - - while IFS= read -r required; do - if [[ ! "${required}" =~ ^[a-z0-9][a-z0-9+.*-]*$ ]]; then - echo "Invalid required package pattern: ${required}" >&2 - return 1 - fi - while IFS= read -r marked_for_removal; do - # shellcheck disable=SC2053 # required entries intentionally support package-name globs. - if [[ "${marked_for_removal}" == ${required} ]]; then - echo "Marked-for-removal package is protected by required pattern ${required}: ${marked_for_removal}" >&2 - return 1 - fi - done < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") - done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") -} - -normalize_package_name() { - printf '%s\n' "${1%%:*}" -} - -package_is_marked_for_removal() { - local package - package="$(normalize_package_name "$1")" - grep -Fxq "${package}" < <(read_package_list "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") -} - -package_is_required() { - local package required - package="$(normalize_package_name "$1")" - while IFS= read -r required; do - # shellcheck disable=SC2053 # required entries intentionally support package-name globs. - if [[ "${package}" == ${required} ]]; then - return 0 - fi - done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") - return 1 -} - -list_installed_packages() { - dpkg-query -W -f='${binary:Package}\t${db:Status-Status}\n' | - while IFS=$'\t' read -r package status; do - [ "${status}" = "installed" ] && printf '%s\n' "${package}" - done -} - -verify_required_packages_installed() { - local installed_file="$1" - local required package matched - - while IFS= read -r required; do - matched=false - while IFS= read -r package; do - package="$(normalize_package_name "${package}")" - # shellcheck disable=SC2053 # required entries intentionally support package-name globs. - if [[ "${package}" == ${required} ]]; then - matched=true - break - fi - done < "${installed_file}" - if [ "${matched}" != "true" ]; then - echo "Required CVM package pattern is not installed: ${required}" >&2 - return 1 - fi - done < <(read_package_list "${REQUIRED_PACKAGES_FILE}") -} - -parse_simulated_removals() { - local action package - while read -r action package _; do - case "${action}" in - Remv | Purg) - [ -n "${package}" ] && printf '%s\n' "${package}" - ;; - esac - done -} - -validate_removal_plan() { - local removal_file="$1" - local essential_file="$2" - local package normalized - - [ -s "${removal_file}" ] || { - echo "apt simulation did not report any package removals" >&2 - return 1 - } - - while IFS= read -r package; do - normalized="$(normalize_package_name "${package}")" - if package_is_required "${normalized}"; then - echo "apt simulation would remove required CVM package: ${package}" >&2 - return 1 - fi - if ! package_is_marked_for_removal "${normalized}"; then - echo "apt simulation would remove unspecified package: ${package}" >&2 - return 1 - fi - if grep -Fxq "${normalized}" "${essential_file}"; then - echo "apt simulation would remove Essential package: ${package}" >&2 - return 1 - fi - done < "${removal_file}" -} - -verify_all_marked_for_removal_planned() { - local installed_marked_for_removal_file="$1" - local removal_file="$2" - local package normalized - - while IFS= read -r package; do - normalized="$(normalize_package_name "${package}")" - if ! grep -Fxq "${normalized}" < <(sed 's/:.*//' "${removal_file}"); then - echo "apt simulation omitted installed marked-for-removal package: ${package}" >&2 - return 1 - fi - done < "${installed_marked_for_removal_file}" -} - -verify_no_marked_for_removal_installed() { - local installed_file="$1" - local package - - while IFS= read -r package; do - if package_is_marked_for_removal "${package}"; then - echo "Marked-for-removal package remained installed after pruning: ${package}" >&2 - return 1 - fi - done < "${installed_file}" -} - -main() { - local work_dir installed_before installed_after installed_marked_for_removal - local unspecified_packages essential_packages simulation_output removal_plan audit_output - local package normalized - local -a purge_packages=() - - if ! should_prune_ubuntu_2604_server_cvm; then - echo "Skipping package pruning: only Ubuntu 26.04 server-cvm builds with the cvm feature are supported" - return 0 - fi - - validate_package_lists - - work_dir="$(mktemp -d)" - PRUNE_WORK_DIR="${work_dir}" - trap 'rm -rf "${PRUNE_WORK_DIR}"' EXIT - installed_before="${work_dir}/installed-before.txt" - installed_after="${work_dir}/installed-after.txt" - installed_marked_for_removal="${work_dir}/installed-marked-for-removal.txt" - unspecified_packages="${work_dir}/unspecified-packages.txt" - essential_packages="${work_dir}/essential-packages.txt" - simulation_output="${work_dir}/apt-simulation.txt" - removal_plan="${work_dir}/removal-plan.txt" - audit_output="${work_dir}/dpkg-audit.txt" - - list_installed_packages | sort -u > "${installed_before}" - verify_required_packages_installed "${installed_before}" - - while IFS= read -r package; do - normalized="$(normalize_package_name "${package}")" - if package_is_marked_for_removal "${normalized}"; then - printf '%s\n' "${package}" >> "${installed_marked_for_removal}" - else - printf '%s\n' "${package}" >> "${unspecified_packages}" - fi - if [ "$(dpkg-query -W -f='${Essential}' "${package}")" = "yes" ]; then - printf '%s\n' "${normalized}" >> "${essential_packages}" - fi - done < "${installed_before}" - - if [ -s "${unspecified_packages}" ]; then - xargs -r -n 100 apt-mark -o DPkg::Lock::Timeout=300 manual < "${unspecified_packages}" - fi - - if [ ! -s "${installed_marked_for_removal}" ]; then - echo "No installed server-cvm packages marked for removal were found" - dpkg --audit > "${audit_output}" - [ ! -s "${audit_output}" ] || { - cat "${audit_output}" >&2 - return 1 - } - apt-get -o DPkg::Lock::Timeout=300 check - return 0 - fi - - mapfile -t purge_packages < "${installed_marked_for_removal}" - if ! DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 --simulate purge "${purge_packages[@]}" > "${simulation_output}" 2>&1; then - cat "${simulation_output}" >&2 - echo "apt purge simulation failed" >&2 - return 1 - fi - - parse_simulated_removals < "${simulation_output}" | sort -u > "${removal_plan}" - validate_removal_plan "${removal_plan}" "${essential_packages}" - verify_all_marked_for_removal_planned "${installed_marked_for_removal}" "${removal_plan}" - - echo "Purging $(wc -l < "${installed_marked_for_removal}") installed server-cvm packages marked for removal" - DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove "${purge_packages[@]}" - - list_installed_packages | sort -u > "${installed_after}" - verify_required_packages_installed "${installed_after}" - verify_no_marked_for_removal_installed "${installed_after}" - - dpkg --audit > "${audit_output}" - if [ -s "${audit_output}" ]; then - cat "${audit_output}" >&2 - return 1 - fi - apt-get -o DPkg::Lock::Timeout=300 check -} - -if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then - set -euo pipefail - main "$@" -fi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh new file mode 100644 index 00000000000..8f653869d88 --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" +REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" + +readPackageList() { + sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "$1" +} + +verifyRequiredPackagesInstalled() { + local required + + while IFS= read -r required; do + if ! dpkg-query -W -f='${db:Status-Status}\n' "${required}" 2>/dev/null | grep -Fxq "installed"; then + echo "Required CVM package pattern is not installed: ${required}" >&2 + return 1 + fi + done < <(readPackageList "${REQUIRED_PACKAGES_FILE}") +} + +main() { + local package + local -a purge_packages=() + + [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { + echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 + return 1 + } + [ -s "${REQUIRED_PACKAGES_FILE}" ] || { + echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 + return 1 + } + + while IFS= read -r package; do + if [ "$(dpkg-query -W -f='${db:Status-Status}' "${package}" 2>/dev/null || true)" = "installed" ]; then + purge_packages+=("${package}") + fi + done < <(readPackageList "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") + + if [ "${#purge_packages[@]}" -gt 0 ]; then + echo "Purging ${#purge_packages[@]} installed server-cvm packages marked for removal" + DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove "${purge_packages[@]}" + else + echo "No installed server-cvm packages marked for removal were found" + fi + + verifyRequiredPackagesInstalled +} + +main "$@" diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index a69074dd051..82050a02d90 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -361,8 +361,8 @@ }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/prune-server-cvm.sh", - "destination": "/home/packer/prune-server-cvm.sh" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh", + "destination": "/home/packer/trim-2604-cvm-packages.sh" }, { "type": "file", From 14dae515619f9153fbbc7e36fd2e6aa5b74b687a Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 11:19:23 -0400 Subject: [PATCH 12/47] fox --- .pipelines/templates/.builder-release-template.yaml | 2 +- .../linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh | 2 +- vhdbuilder/packer/pre-install-dependencies.sh | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 146706869e2..fbda70f80b9 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -76,7 +76,7 @@ steps: - bash: | SKU_NAME=${OS_VERSION} && \ if grep -q "minimal" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}minimal"; fi && \ - if [[ "${OS_SKU}" == "Ubuntu" && "${OS_VERSION}" == "26.04" && "${IMG_SKU}" == "server-cvm" ]] && tr ',' '\n' <<<"${FEATURE_FLAGS}" | grep -Fxq "cvm"; then SKU_NAME="${SKU_NAME}minimal"; fi && \ + if [[ "${OS_SKU}" == "Ubuntu" && "${OS_VERSION}" == "26.04" && "${IMG_SKU}" == "server-cvm" ]]; then SKU_NAME="${SKU_NAME}minimal"; fi && \ if [[ "${HYPERV_GENERATION,,}" == "v2" ]]; then SKU_NAME="${SKU_NAME}gen2"; fi && \ if [[ "${ARCHITECTURE,,}" == "arm64" ]]; then SKU_NAME="${SKU_NAME}arm64"; fi && \ if grep -q "NVIDIA_GB" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}gb"; fi && \ diff --git a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh index 1ade5582bf4..a1dece1d606 100755 --- a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh +++ b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh @@ -108,7 +108,7 @@ installDeps() { pkg_list=(apparmor-utils bind9-dnsutils ca-certificates ceph-common cgroup-lite cifs-utils conntrack cracklib-runtime ebtables ethtool glusterfs-client htop init-system-helpers inotify-tools iotop iproute2 ipset iptables nftables jq libpam-pwquality libpwquality-tools mount nfs-common pigz socat sysfsutils sysstat util-linux xz-utils netcat-openbsd zip rng-tools kmod gcc make dkms initramfs-tools linux-headers-$(uname -r)) if [ "${OSVERSION}" = "26.04" ]; then - if isMinimalImage || { [ "${IMG_SKU:-}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "cvm"; }; then + if isMinimalImage || [ "${IMG_SKU:-}" = "server-cvm" ]; then # libc6-dev is needed for GPU driver installation at runtime and is not included on the 26.04 minimal base image pkg_list+=(libc6-dev) # cron/crontab is needed by init-aks-cloud.sh (RCV1P) since we create a ca-refresh cron job and is not included on the 26.04 minimal base image diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 05c13ece6fa..a3ba5e3157d 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -5,8 +5,7 @@ OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && - [ "${IMG_SKU:-}" = "server-cvm" ] && - tr ',' '\n' <<<"${FEATURE_FLAGS:-}" | grep -Fxq "cvm"; then + [ "${IMG_SKU:-}" = "server-cvm" ]; then /bin/bash /home/packer/trim-2604-cvm-packages.sh fi @@ -45,7 +44,7 @@ capture_benchmark "${SCRIPT_NAME}_source_packer_files_and_declare_variables" copyPackerFiles # Install required dependencies needed to build minimal images if needed (currently only Ubuntu 26.04) -if isUbuntu "$OS" && { isMinimalImage || { [ "${OS_VERSION}" = "26.04" ] && [ "${IMG_SKU}" = "server-cvm" ] && tr ',' '\n' <<<"${FEATURE_FLAGS}" | grep -Fxq "cvm"; }; }; then +if isUbuntu "$OS" && { isMinimalImage || { [ "${OS_VERSION}" = "26.04" ] && [ "${IMG_SKU}" = "server-cvm" ]; }; }; then installMinimalBuildDeps fi From 7a7f2f80f61ee4cf7fe7c67c5774af8f7bd2cc9c Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 12:10:22 -0400 Subject: [PATCH 13/47] fix --- vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 8f653869d88..81a83270be1 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -41,7 +41,7 @@ main() { if [ "${#purge_packages[@]}" -gt 0 ]; then echo "Purging ${#purge_packages[@]} installed server-cvm packages marked for removal" - DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove "${purge_packages[@]}" + DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential "${purge_packages[@]}" else echo "No installed server-cvm packages marked for removal were found" fi From 56d56d8d3fde3a2494bb58b9c80af52083583458 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 12:57:26 -0400 Subject: [PATCH 14/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index f87b65e6046..c80832825e9 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -62,7 +62,6 @@ ibverbs-providers ieee-data inetutils-telnet info -init install-info iptables iputils-ping From 19c070129f2b144163299855a4be56872c0f2466 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 14:21:44 -0400 Subject: [PATCH 15/47] fix --- vhdbuilder/packer/pre-install-dependencies.sh | 7 +++ .../deferred-systemd-packages.txt | 4 ++ .../marked-for-removal-packages.txt | 4 -- .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 43 ++++++++++++++----- vhdbuilder/packer/vhd-image-builder-cvm.json | 5 +++ 5 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index a3ba5e3157d..f342ec6a7b2 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -312,6 +312,13 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then fi fi capture_benchmark "${SCRIPT_NAME}_purge_ubuntu_kernel_if_2204" + +if [ "$OS" = "UBUNTU" ] && + [ "$OS_VERSION" = "26.04" ] && + [ "${IMG_SKU:-}" = "server-cvm" ]; then + /bin/bash /home/packer/trim-2604-cvm-packages.sh --systemd-packages +fi + echo "pre-install-dependencies step finished successfully" capture_benchmark "${SCRIPT_NAME}_overall" true process_benchmarks diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt new file mode 100644 index 00000000000..c13d3cd5b2a --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt @@ -0,0 +1,4 @@ +libnss-systemd +python3-systemd +systemd-cryptsetup +systemd-hwe-hwdb diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index c80832825e9..cc991498d07 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -160,7 +160,6 @@ libnfnetlink0 libnl-route-3-200 libnpth0t64 libnspr4 -libnss-systemd libnss3 libntfs-3g89t64 libnuma1 @@ -278,7 +277,6 @@ python3-s3transfer python3-service-identity python3-six python3-software-properties -python3-systemd python3-twisted python3-uc-micro python3-zope.interface @@ -292,8 +290,6 @@ shared-mime-info software-properties-common sos strace -systemd-cryptsetup -systemd-hwe-hwdb tcl tcl8.6 tcpdump diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 81a83270be1..819eaf136d6 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -3,6 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" +DEFERRED_SYSTEMD_PACKAGES_FILE="${SCRIPT_DIR}/deferred-systemd-packages.txt" REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" readPackageList() { @@ -20,16 +21,14 @@ verifyRequiredPackagesInstalled() { done < <(readPackageList "${REQUIRED_PACKAGES_FILE}") } -main() { +purgeInstalledPackages() { + local package_list_file="$1" + local package_description="$2" local package local -a purge_packages=() - [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { - echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 - return 1 - } - [ -s "${REQUIRED_PACKAGES_FILE}" ] || { - echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 + [ -s "${package_list_file}" ] || { + echo "Package list is missing or empty: ${package_list_file}" >&2 return 1 } @@ -37,14 +36,38 @@ main() { if [ "$(dpkg-query -W -f='${db:Status-Status}' "${package}" 2>/dev/null || true)" = "installed" ]; then purge_packages+=("${package}") fi - done < <(readPackageList "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") + done < <(readPackageList "${package_list_file}") if [ "${#purge_packages[@]}" -gt 0 ]; then - echo "Purging ${#purge_packages[@]} installed server-cvm packages marked for removal" + echo "Purging ${#purge_packages[@]} installed ${package_description}" DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential "${purge_packages[@]}" else - echo "No installed server-cvm packages marked for removal were found" + echo "No installed ${package_description} were found" fi +} + +removeSystemdPackages() { + purgeInstalledPackages "${DEFERRED_SYSTEMD_PACKAGES_FILE}" "deferred systemd-related server-cvm packages" +} + +main() { + [ -s "${REQUIRED_PACKAGES_FILE}" ] || { + echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 + return 1 + } + + case "${1:-}" in + "") + purgeInstalledPackages "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" "server-cvm packages marked for removal" + ;; + --systemd-packages) + removeSystemdPackages + ;; + *) + echo "Unsupported argument: $1" >&2 + return 1 + ;; + esac verifyRequiredPackagesInstalled } diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 82050a02d90..84c942797cc 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -369,6 +369,11 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt", "destination": "/home/packer/marked-for-removal-packages.txt" }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt", + "destination": "/home/packer/deferred-systemd-packages.txt" + }, { "type": "file", "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", From ccbb668d26721c8957531ecc9a1b9a7f81fbd9ed Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 15:16:12 -0400 Subject: [PATCH 16/47] fix --- vhdbuilder/packer/pre-install-dependencies.sh | 18 +++----- .../deferred-systemd-packages.txt | 4 -- .../marked-for-removal-packages.txt | 4 ++ .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 43 +++++-------------- vhdbuilder/packer/vhd-image-builder-cvm.json | 5 --- 5 files changed, 20 insertions(+), 54 deletions(-) delete mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index f342ec6a7b2..e2df9a62b69 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -3,12 +3,6 @@ OS=$(sort -r /etc/*-release | sed -n 's/^ID=//p' | head -n1 | tr -d '"' | tr '[: OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') -if [ "$OS" = "UBUNTU" ] && - [ "$OS_VERSION" = "26.04" ] && - [ "${IMG_SKU:-}" = "server-cvm" ]; then - /bin/bash /home/packer/trim-2604-cvm-packages.sh -fi - THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" #the following sed removes all comments of the format {{/* */}} @@ -136,6 +130,12 @@ else apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT apt_get_dist_upgrade || exit $ERR_APT_DIST_UPGRADE_TIMEOUT + if [ "$OS" = "UBUNTU" ] && + [ "$OS_VERSION" = "26.04" ] && + [ "${IMG_SKU:-}" = "server-cvm" ]; then + /bin/bash /home/packer/trim-2604-cvm-packages.sh + fi + # shellcheck disable=SC3010 if [[ "${ENABLE_FIPS,,}" == "true" ]]; then # This is FIPS Install for Ubuntu, it purges non FIPS Kernel and attaches UA FIPS Updates @@ -313,12 +313,6 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then fi capture_benchmark "${SCRIPT_NAME}_purge_ubuntu_kernel_if_2204" -if [ "$OS" = "UBUNTU" ] && - [ "$OS_VERSION" = "26.04" ] && - [ "${IMG_SKU:-}" = "server-cvm" ]; then - /bin/bash /home/packer/trim-2604-cvm-packages.sh --systemd-packages -fi - echo "pre-install-dependencies step finished successfully" capture_benchmark "${SCRIPT_NAME}_overall" true process_benchmarks diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt deleted file mode 100644 index c13d3cd5b2a..00000000000 --- a/vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt +++ /dev/null @@ -1,4 +0,0 @@ -libnss-systemd -python3-systemd -systemd-cryptsetup -systemd-hwe-hwdb diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index cc991498d07..c80832825e9 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -160,6 +160,7 @@ libnfnetlink0 libnl-route-3-200 libnpth0t64 libnspr4 +libnss-systemd libnss3 libntfs-3g89t64 libnuma1 @@ -277,6 +278,7 @@ python3-s3transfer python3-service-identity python3-six python3-software-properties +python3-systemd python3-twisted python3-uc-micro python3-zope.interface @@ -290,6 +292,8 @@ shared-mime-info software-properties-common sos strace +systemd-cryptsetup +systemd-hwe-hwdb tcl tcl8.6 tcpdump diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 819eaf136d6..81a83270be1 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -3,7 +3,6 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" -DEFERRED_SYSTEMD_PACKAGES_FILE="${SCRIPT_DIR}/deferred-systemd-packages.txt" REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" readPackageList() { @@ -21,14 +20,16 @@ verifyRequiredPackagesInstalled() { done < <(readPackageList "${REQUIRED_PACKAGES_FILE}") } -purgeInstalledPackages() { - local package_list_file="$1" - local package_description="$2" +main() { local package local -a purge_packages=() - [ -s "${package_list_file}" ] || { - echo "Package list is missing or empty: ${package_list_file}" >&2 + [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { + echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 + return 1 + } + [ -s "${REQUIRED_PACKAGES_FILE}" ] || { + echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 return 1 } @@ -36,38 +37,14 @@ purgeInstalledPackages() { if [ "$(dpkg-query -W -f='${db:Status-Status}' "${package}" 2>/dev/null || true)" = "installed" ]; then purge_packages+=("${package}") fi - done < <(readPackageList "${package_list_file}") + done < <(readPackageList "${MARKED_FOR_REMOVAL_PACKAGES_FILE}") if [ "${#purge_packages[@]}" -gt 0 ]; then - echo "Purging ${#purge_packages[@]} installed ${package_description}" + echo "Purging ${#purge_packages[@]} installed server-cvm packages marked for removal" DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential "${purge_packages[@]}" else - echo "No installed ${package_description} were found" + echo "No installed server-cvm packages marked for removal were found" fi -} - -removeSystemdPackages() { - purgeInstalledPackages "${DEFERRED_SYSTEMD_PACKAGES_FILE}" "deferred systemd-related server-cvm packages" -} - -main() { - [ -s "${REQUIRED_PACKAGES_FILE}" ] || { - echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 - return 1 - } - - case "${1:-}" in - "") - purgeInstalledPackages "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" "server-cvm packages marked for removal" - ;; - --systemd-packages) - removeSystemdPackages - ;; - *) - echo "Unsupported argument: $1" >&2 - return 1 - ;; - esac verifyRequiredPackagesInstalled } diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 84c942797cc..82050a02d90 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -369,11 +369,6 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt", "destination": "/home/packer/marked-for-removal-packages.txt" }, - { - "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/deferred-systemd-packages.txt", - "destination": "/home/packer/deferred-systemd-packages.txt" - }, { "type": "file", "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", From 2b743e87eb91300681c4b81d79f90866de4bb59e Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 16:49:45 -0400 Subject: [PATCH 17/47] fix --- vhdbuilder/packer/pre-install-dependencies.sh | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index e2df9a62b69..15dccb8f5b9 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -22,6 +22,76 @@ CPU_ARCH=$(getCPUArch) #amd64 or arm64 VHD_LOGS_FILEPATH=/opt/azure/vhd-install.complete COMPONENTS_FILEPATH=/opt/azure/components.json PERFORMANCE_DATA_FILE=/opt/azure/vhd-build-performance-data.json + +collectUbuntu2604RebootDiagnostics() { + local stage="$1" + + if ! isUbuntu "$OS" || [ "$OS_VERSION" != "26.04" ]; then + return + fi + + echo "===== Ubuntu 26.04 reboot diagnostics: ${stage} =====" + echo "--- system state ---" + date --iso-8601=seconds || true + uptime || true + systemctl is-system-running || true + systemctl --failed --no-pager --full || true + systemctl list-jobs --no-pager || true + busctl --system list --no-pager || true + loginctl list-sessions --no-pager || true + + echo "--- shutdown inhibitors ---" + systemd-inhibit --list --no-pager || true + ls -la /run/systemd/inhibit || true + + echo "--- upgrade and systemd services ---" + systemctl status \ + systemd-logind.service \ + dbus.service \ + unattended-upgrades.service \ + apt-daily.service \ + apt-daily-upgrade.service \ + --no-pager --full || true + systemctl show \ + systemd-logind.service \ + dbus.service \ + unattended-upgrades.service \ + apt-daily.service \ + apt-daily-upgrade.service \ + --property=Id,LoadState,ActiveState,SubState,MainPID,ExecMainStatus,Result \ + --no-pager || true + + echo "--- package-manager processes and locks ---" + dpkg-query -W \ + -f='${binary:Package}\t${Version}\t${db:Status-Abbrev}\n' \ + systemd systemd-sysv dbus unattended-upgrades 2>&1 || true + # Keep start time and parent PID in the output; pgrep does not provide both. + # shellcheck disable=SC2009 + ps -eo pid,ppid,state,lstart,cmd --sort=pid | grep -E '[a]pt|[d]pkg|[u]nattended|[p]ackagekit' || true + if command -v lslocks >/dev/null 2>&1; then + lslocks || true + fi + if command -v fuser >/dev/null 2>&1; then + fuser -v \ + /var/lib/dpkg/lock \ + /var/lib/dpkg/lock-frontend \ + /var/lib/apt/lists/lock \ + /var/cache/apt/archives/lock || true + fi + dpkg --audit || true + + echo "--- recent system and upgrade journal ---" + journalctl -b --since "-15 minutes" \ + -u systemd-logind.service \ + -u dbus.service \ + -u unattended-upgrades.service \ + -u apt-daily.service \ + -u apt-daily-upgrade.service \ + --no-pager || true + journalctl -b --since "-15 minutes" --priority=warning --no-pager || true + echo "===== End Ubuntu 26.04 reboot diagnostics: ${stage} =====" +} + #this is used by post build test to check whether the compoenents do indeed exist cat components.json > ${COMPONENTS_FILEPATH} echo "Starting build on " $(date) > ${VHD_LOGS_FILEPATH} @@ -129,6 +199,7 @@ else # Run apt dist get upgrade to install packages/kernels apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT apt_get_dist_upgrade || exit $ERR_APT_DIST_UPGRADE_TIMEOUT + collectUbuntu2604RebootDiagnostics "after dist-upgrade" if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && @@ -316,3 +387,4 @@ capture_benchmark "${SCRIPT_NAME}_purge_ubuntu_kernel_if_2204" echo "pre-install-dependencies step finished successfully" capture_benchmark "${SCRIPT_NAME}_overall" true process_benchmarks +collectUbuntu2604RebootDiagnostics "immediately before first Packer reboot" From 6155e9b461f63883e27831f4d77a8cd59cb949a4 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 17:47:49 -0400 Subject: [PATCH 18/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index c80832825e9..c081782ca91 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -67,7 +67,6 @@ iptables iputils-ping iputils-tracepath iso-codes -jq kpartx krb5-locales landscape-common @@ -131,7 +130,6 @@ libip6tc2 libisns0t64 libjcat1 libjemalloc2 -libjq1 libjson-glib-1.0-0 libjson-glib-1.0-common libksba8 From 5b84138ad573302b481867ef72448f9541a11da2 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Tue, 15 Sep 2026 17:55:44 -0400 Subject: [PATCH 19/47] fix --- vhdbuilder/packer/install-dependencies.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vhdbuilder/packer/install-dependencies.sh b/vhdbuilder/packer/install-dependencies.sh index dd4d4cd18a8..87cacc30998 100644 --- a/vhdbuilder/packer/install-dependencies.sh +++ b/vhdbuilder/packer/install-dependencies.sh @@ -1211,6 +1211,9 @@ if isMarinerOrAzureLinux "$OS" && ! isAzureLinuxOSGuard "$OS" "$OS_VARIANT"; the activateNfConntrack elif [ "${OS}" = "${UBUNTU_OS_NAME}" ]; then updateAptWithMicrosoftPkg + if [ "${OS_VERSION}" = "26.04" ]; then + find /var/lib/apt/lists -maxdepth 2 -type f -name 'developer.download.nvidia.com_compute_cuda_repos_ubuntu2604_*' -delete + fi updateAptWithNvidiaPkg fi capture_benchmark "${SCRIPT_NAME}_handle_os_specific_configurations" From fdccef1d8ecb2b8f7f24c1e85e4c91ee023a5552 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 10:31:44 -0400 Subject: [PATCH 20/47] fix --- vhdbuilder/packer/install-dependencies.sh | 3 - vhdbuilder/packer/pre-install-dependencies.sh | 71 ------------------- .../marked-for-removal-packages.txt | 1 - 3 files changed, 75 deletions(-) diff --git a/vhdbuilder/packer/install-dependencies.sh b/vhdbuilder/packer/install-dependencies.sh index 87cacc30998..dd4d4cd18a8 100644 --- a/vhdbuilder/packer/install-dependencies.sh +++ b/vhdbuilder/packer/install-dependencies.sh @@ -1211,9 +1211,6 @@ if isMarinerOrAzureLinux "$OS" && ! isAzureLinuxOSGuard "$OS" "$OS_VARIANT"; the activateNfConntrack elif [ "${OS}" = "${UBUNTU_OS_NAME}" ]; then updateAptWithMicrosoftPkg - if [ "${OS_VERSION}" = "26.04" ]; then - find /var/lib/apt/lists -maxdepth 2 -type f -name 'developer.download.nvidia.com_compute_cuda_repos_ubuntu2604_*' -delete - fi updateAptWithNvidiaPkg fi capture_benchmark "${SCRIPT_NAME}_handle_os_specific_configurations" diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 15dccb8f5b9..0a1c28d3dce 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -23,75 +23,6 @@ VHD_LOGS_FILEPATH=/opt/azure/vhd-install.complete COMPONENTS_FILEPATH=/opt/azure/components.json PERFORMANCE_DATA_FILE=/opt/azure/vhd-build-performance-data.json -collectUbuntu2604RebootDiagnostics() { - local stage="$1" - - if ! isUbuntu "$OS" || [ "$OS_VERSION" != "26.04" ]; then - return - fi - - echo "===== Ubuntu 26.04 reboot diagnostics: ${stage} =====" - echo "--- system state ---" - date --iso-8601=seconds || true - uptime || true - systemctl is-system-running || true - systemctl --failed --no-pager --full || true - systemctl list-jobs --no-pager || true - busctl --system list --no-pager || true - loginctl list-sessions --no-pager || true - - echo "--- shutdown inhibitors ---" - systemd-inhibit --list --no-pager || true - ls -la /run/systemd/inhibit || true - - echo "--- upgrade and systemd services ---" - systemctl status \ - systemd-logind.service \ - dbus.service \ - unattended-upgrades.service \ - apt-daily.service \ - apt-daily-upgrade.service \ - --no-pager --full || true - systemctl show \ - systemd-logind.service \ - dbus.service \ - unattended-upgrades.service \ - apt-daily.service \ - apt-daily-upgrade.service \ - --property=Id,LoadState,ActiveState,SubState,MainPID,ExecMainStatus,Result \ - --no-pager || true - - echo "--- package-manager processes and locks ---" - dpkg-query -W \ - -f='${binary:Package}\t${Version}\t${db:Status-Abbrev}\n' \ - systemd systemd-sysv dbus unattended-upgrades 2>&1 || true - # Keep start time and parent PID in the output; pgrep does not provide both. - # shellcheck disable=SC2009 - ps -eo pid,ppid,state,lstart,cmd --sort=pid | grep -E '[a]pt|[d]pkg|[u]nattended|[p]ackagekit' || true - if command -v lslocks >/dev/null 2>&1; then - lslocks || true - fi - if command -v fuser >/dev/null 2>&1; then - fuser -v \ - /var/lib/dpkg/lock \ - /var/lib/dpkg/lock-frontend \ - /var/lib/apt/lists/lock \ - /var/cache/apt/archives/lock || true - fi - dpkg --audit || true - - echo "--- recent system and upgrade journal ---" - journalctl -b --since "-15 minutes" \ - -u systemd-logind.service \ - -u dbus.service \ - -u unattended-upgrades.service \ - -u apt-daily.service \ - -u apt-daily-upgrade.service \ - --no-pager || true - journalctl -b --since "-15 minutes" --priority=warning --no-pager || true - echo "===== End Ubuntu 26.04 reboot diagnostics: ${stage} =====" -} - #this is used by post build test to check whether the compoenents do indeed exist cat components.json > ${COMPONENTS_FILEPATH} echo "Starting build on " $(date) > ${VHD_LOGS_FILEPATH} @@ -199,7 +130,6 @@ else # Run apt dist get upgrade to install packages/kernels apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT apt_get_dist_upgrade || exit $ERR_APT_DIST_UPGRADE_TIMEOUT - collectUbuntu2604RebootDiagnostics "after dist-upgrade" if [ "$OS" = "UBUNTU" ] && [ "$OS_VERSION" = "26.04" ] && @@ -387,4 +317,3 @@ capture_benchmark "${SCRIPT_NAME}_purge_ubuntu_kernel_if_2204" echo "pre-install-dependencies step finished successfully" capture_benchmark "${SCRIPT_NAME}_overall" true process_benchmarks -collectUbuntu2604RebootDiagnostics "immediately before first Packer reboot" diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index c081782ca91..f065a74c70c 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -162,7 +162,6 @@ libnss-systemd libnss3 libntfs-3g89t64 libnuma1 -libonig5 libopeniscsiusr libpackagekit-glib2-18 libpam-cap From 5ca4363d84397a7cc2a458ef83f5ff7c539080fa Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 10:56:11 -0400 Subject: [PATCH 21/47] fix --- .../linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh index a1dece1d606..cd64c90e399 100755 --- a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh +++ b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh @@ -213,7 +213,9 @@ updateAptWithNvidiaPkg() { elif [ "${UBUNTU_RELEASE}" = "24.04" ]; then nvidia_ubuntu_release="ubuntu2404" elif [ "${UBUNTU_RELEASE}" = "26.04" ]; then - nvidia_ubuntu_release="ubuntu2604" + # TODO: Restore ubuntu2604 after NVIDIA republishes a valid Packages index. + nvidia_ubuntu_release="ubuntu2404" + echo "Using the Ubuntu 24.04 NVIDIA repository while the Ubuntu 26.04 index is malformed" else echo "NVIDIA repo setup is not supported on Ubuntu ${UBUNTU_RELEASE}" return @@ -224,7 +226,7 @@ updateAptWithNvidiaPkg() { # Add NVIDIA repository local nvidia_gpg_key_name="3bf863cc.pub" - if [ "${UBUNTU_RELEASE}" = "26.04" ]; then + if [ "${nvidia_ubuntu_release}" = "ubuntu2604" ]; then nvidia_gpg_key_name="60DF8A40.pub" fi local nvidia_gpg_key_url="https://developer.download.nvidia.com/compute/cuda/repos/${nvidia_ubuntu_release}/${repo_arch}/${nvidia_gpg_key_name}" From f66466f3d5644df5d0bad3a9fb9aefd812b48ed9 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 11:19:12 -0400 Subject: [PATCH 22/47] fix --- .pipelines/.vsts-vhd-builder-release.yaml | 2 +- .pipelines/templates/.builder-release-template.yaml | 1 - .../cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh | 2 +- vhdbuilder/packer/pre-install-dependencies.sh | 7 ++++--- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 1 - vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 1 + 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.pipelines/.vsts-vhd-builder-release.yaml b/.pipelines/.vsts-vhd-builder-release.yaml index 0eaa37a2cde..234dff8c499 100644 --- a/.pipelines/.vsts-vhd-builder-release.yaml +++ b/.pipelines/.vsts-vhd-builder-release.yaml @@ -829,7 +829,7 @@ stages: echo '##vso[task.setvariable variable=IMG_VERSION]latest' echo '##vso[task.setvariable variable=HYPERV_GENERATION]V2' echo '##vso[task.setvariable variable=AZURE_VM_SIZE]Standard_DC16ads_v5' - echo '##vso[task.setvariable variable=FEATURE_FLAGS]cvm' + echo '##vso[task.setvariable variable=FEATURE_FLAGS]minimal,cvm' echo '##vso[task.setvariable variable=ARCHITECTURE]X86_64' echo '##vso[task.setvariable variable=ENABLE_FIPS]False' echo '##vso[task.setvariable variable=ENABLE_TRUSTED_LAUNCH]False' diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index fbda70f80b9..7069f8d5ed7 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -76,7 +76,6 @@ steps: - bash: | SKU_NAME=${OS_VERSION} && \ if grep -q "minimal" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}minimal"; fi && \ - if [[ "${OS_SKU}" == "Ubuntu" && "${OS_VERSION}" == "26.04" && "${IMG_SKU}" == "server-cvm" ]]; then SKU_NAME="${SKU_NAME}minimal"; fi && \ if [[ "${HYPERV_GENERATION,,}" == "v2" ]]; then SKU_NAME="${SKU_NAME}gen2"; fi && \ if [[ "${ARCHITECTURE,,}" == "arm64" ]]; then SKU_NAME="${SKU_NAME}arm64"; fi && \ if grep -q "NVIDIA_GB" <<< "$FEATURE_FLAGS"; then SKU_NAME="${SKU_NAME}gb"; fi && \ diff --git a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh index cd64c90e399..7cbf7726562 100755 --- a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh +++ b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh @@ -108,7 +108,7 @@ installDeps() { pkg_list=(apparmor-utils bind9-dnsutils ca-certificates ceph-common cgroup-lite cifs-utils conntrack cracklib-runtime ebtables ethtool glusterfs-client htop init-system-helpers inotify-tools iotop iproute2 ipset iptables nftables jq libpam-pwquality libpwquality-tools mount nfs-common pigz socat sysfsutils sysstat util-linux xz-utils netcat-openbsd zip rng-tools kmod gcc make dkms initramfs-tools linux-headers-$(uname -r)) if [ "${OSVERSION}" = "26.04" ]; then - if isMinimalImage || [ "${IMG_SKU:-}" = "server-cvm" ]; then + if isMinimalImage; then # libc6-dev is needed for GPU driver installation at runtime and is not included on the 26.04 minimal base image pkg_list+=(libc6-dev) # cron/crontab is needed by init-aks-cloud.sh (RCV1P) since we create a ca-refresh cron job and is not included on the 26.04 minimal base image diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 0a1c28d3dce..59356cbe4fb 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -39,7 +39,7 @@ capture_benchmark "${SCRIPT_NAME}_source_packer_files_and_declare_variables" copyPackerFiles # Install required dependencies needed to build minimal images if needed (currently only Ubuntu 26.04) -if isUbuntu "$OS" && { isMinimalImage || { [ "${OS_VERSION}" = "26.04" ] && [ "${IMG_SKU}" = "server-cvm" ]; }; }; then +if isUbuntu "$OS" && isMinimalImage; then installMinimalBuildDeps fi @@ -131,9 +131,10 @@ else apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT apt_get_dist_upgrade || exit $ERR_APT_DIST_UPGRADE_TIMEOUT - if [ "$OS" = "UBUNTU" ] && + if isUbuntu "$OS" && [ "$OS_VERSION" = "26.04" ] && - [ "${IMG_SKU:-}" = "server-cvm" ]; then + isMinimalImage && + grep -q "cvm" <<< "$FEATURE_FLAGS"; then /bin/bash /home/packer/trim-2604-cvm-packages.sh fi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index f065a74c70c..0ae7e751055 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -281,7 +281,6 @@ python3-uc-micro python3-zope.interface python3.14-gdbm rsync -rsyslog sg3-utils sg3-utils-udev sgml-base diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index f50be581bac..b0ec4664ff8 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -19,3 +19,4 @@ linux-tools-*-azure linux-tools-*-azure-fde linux-tools-azure-fde nullboot +rsyslog From 844440100e934e384e2258dc28dd107847116765 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 11:33:10 -0400 Subject: [PATCH 23/47] fix --- vhdbuilder/packer/pre-install-dependencies.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 59356cbe4fb..3d5df1fd28b 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -39,7 +39,7 @@ capture_benchmark "${SCRIPT_NAME}_source_packer_files_and_declare_variables" copyPackerFiles # Install required dependencies needed to build minimal images if needed (currently only Ubuntu 26.04) -if isUbuntu "$OS" && isMinimalImage; then +if isMinimalImage && isUbuntu "$OS"; then installMinimalBuildDeps fi @@ -314,7 +314,6 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then fi fi capture_benchmark "${SCRIPT_NAME}_purge_ubuntu_kernel_if_2204" - echo "pre-install-dependencies step finished successfully" capture_benchmark "${SCRIPT_NAME}_overall" true process_benchmarks From dd4fc0e3ed54b0c88f28b2e4a2d723501f8e18a0 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 11:51:42 -0400 Subject: [PATCH 24/47] fix --- .pipelines/templates/.builder-release-template.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 7069f8d5ed7..7cd6698adf2 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,7 +97,6 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -118,7 +117,6 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -230,7 +228,6 @@ steps: make -f packer.mk publish-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Publish VHD - Image Customizer - retryCountOnTaskFailure: 3 env: RESOURCE_GROUP_NAME: $(AZURE_RESOURCE_GROUP_NAME) GENERATE_PUBLISHING_INFO: $(GENERATE_PUBLISHING_INFO) @@ -327,7 +324,6 @@ steps: eq(variables.PREFETCH_COMPATIBLE, 'True') ) displayName: Run Prefetch Optimization and Convert to VHD blob - retryCountOnTaskFailure: 3 env: SUBSCRIPTION_ID: $(SUBSCRIPTION_ID) LOCATION: $(PACKER_BUILD_LOCATION) From 6ebef662cff736cce69012baaa08bce6d295bad2 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 12:14:23 -0400 Subject: [PATCH 25/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 0ae7e751055..056af449ffe 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -101,10 +101,8 @@ libdrm-amdgpu1 libduktape207 libeatmydata1 liberror-perl -libestr0 libevdev2 libevent-core-2.1-7t64 -libfastjson4 libflashrom1 libfribidi0 libftdi1-2 From 4d3421161ac297ad166f40e81ff98ad91280fe50 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 13:07:10 -0400 Subject: [PATCH 26/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 1 + vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 2 ++ vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 1 + 3 files changed, 4 insertions(+) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 056af449ffe..58ac161859c 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -62,6 +62,7 @@ ibverbs-providers ieee-data inetutils-telnet info +init install-info iptables iputils-ping diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index b0ec4664ff8..be1026971e3 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -1,5 +1,6 @@ cryptsetup cryptsetup-bin +jq libcryptsetup12 lsscsi linux-azure-fde @@ -20,3 +21,4 @@ linux-tools-*-azure-fde linux-tools-azure-fde nullboot rsyslog +sudo diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 81a83270be1..1ed7e3283e1 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -46,6 +46,7 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi + apt-mark manual jq rsyslog sudo verifyRequiredPackagesInstalled } From 2ee58b9e0f5d5197db7dc800c124742fc3714793 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 13:58:42 -0400 Subject: [PATCH 27/47] fix --- .pipelines/templates/.builder-release-template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 7cd6698adf2..eacf1678016 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -228,6 +228,7 @@ steps: make -f packer.mk publish-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Publish VHD - Image Customizer + retryCountOnTaskFailure: 3 env: RESOURCE_GROUP_NAME: $(AZURE_RESOURCE_GROUP_NAME) GENERATE_PUBLISHING_INFO: $(GENERATE_PUBLISHING_INFO) @@ -324,6 +325,7 @@ steps: eq(variables.PREFETCH_COMPATIBLE, 'True') ) displayName: Run Prefetch Optimization and Convert to VHD blob + retryCountOnTaskFailure: 3 env: SUBSCRIPTION_ID: $(SUBSCRIPTION_ID) LOCATION: $(PACKER_BUILD_LOCATION) From 7a46e4c9ba029ec7eee641e7458bd30896369e52 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 15:30:04 -0400 Subject: [PATCH 28/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 1 + vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 1 + vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 58ac161859c..e7fb22a5ab0 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -319,5 +319,6 @@ xauth xdg-user-dirs xfsprogs xml-core +xxd zerofree zstd diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index be1026971e3..2cc18d26cd6 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -1,5 +1,6 @@ cryptsetup cryptsetup-bin +curl jq libcryptsetup12 lsscsi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 1ed7e3283e1..9043f288d69 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -46,7 +46,7 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual jq rsyslog sudo + apt-mark manual curl jq rsyslog sudo verifyRequiredPackagesInstalled } From b4a9c99ced7c2062f72d66d376c9ad4b37d8b1c2 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 16:15:13 -0400 Subject: [PATCH 29/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 3 --- vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 1 + vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index e7fb22a5ab0..aefd056d423 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -50,10 +50,8 @@ git-man gnupg gnupg-l10n gnupg-utils -gpg gpg-agent gpg-wks-client -gpgconf gpgsm groff-base hdparm @@ -74,7 +72,6 @@ landscape-common less libaio1t64 libappstream5 -libassuan9 libatasmart4 libbinutils libblockdev-crypto3 diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index 2cc18d26cd6..f65fae02905 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -1,6 +1,7 @@ cryptsetup cryptsetup-bin curl +gpg jq libcryptsetup12 lsscsi diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 9043f288d69..7306a0a5139 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -46,7 +46,7 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual curl jq rsyslog sudo + apt-mark manual curl gpg jq rsyslog sudo verifyRequiredPackagesInstalled } From f8b6d9d5650e5a87687a731a1bbcefdfa90ceb62 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 16:45:09 -0400 Subject: [PATCH 30/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index aefd056d423..9b9068e0853 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -24,7 +24,6 @@ cpio cron cron-daemon-common debconf-i18n -dirmngr dmeventd dosfstools dracut @@ -47,12 +46,6 @@ gdisk gir1.2-packagekitglib-1.0 git git-man -gnupg -gnupg-l10n -gnupg-utils -gpg-agent -gpg-wks-client -gpgsm groff-base hdparm htop From 6cc30f4719bfacad44f4cf1d1933eac397162f60 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Wed, 16 Sep 2026 17:17:47 -0400 Subject: [PATCH 31/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 9b9068e0853..96b91f814b2 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -121,7 +121,6 @@ libjcat1 libjemalloc2 libjson-glib-1.0-0 libjson-glib-1.0-common -libksba8 liblastlog2-2 libllvm21 liblmdb0 @@ -145,7 +144,6 @@ libnetfilter-conntrack3 libnewt0.52 libnfnetlink0 libnl-route-3-200 -libnpth0t64 libnspr4 libnss-systemd libnss3 @@ -224,7 +222,6 @@ packagekit patch perl perl-modules-5.40 -pinentry-curses plymouth plymouth-theme-ubuntu-text polkitd From fc88eda36b2c9fd1013a0669fd96acfd692e4733 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 09:39:59 -0400 Subject: [PATCH 32/47] fix --- .pipelines/scripts/verify_shell.sh | 1 + .../templates/.builder-release-template.yaml | 2 + e2e/scenario/vmss_test.go | 482 ++++++++++++++++++ .../packer/trim_2604_cvm_packages_spec.sh | 106 ++++ .../packer/post-install-dependencies.sh | 4 + vhdbuilder/packer/pre-install-dependencies.sh | 2 - .../marked-for-removal-packages.txt | 2 - .../ubuntu-2604-cvm/required-packages.txt | 2 + .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 42 +- 9 files changed, 628 insertions(+), 15 deletions(-) create mode 100644 e2e/scenario/vmss_test.go create mode 100644 spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh diff --git a/.pipelines/scripts/verify_shell.sh b/.pipelines/scripts/verify_shell.sh index 3bd86949edc..393af1267f6 100755 --- a/.pipelines/scripts/verify_shell.sh +++ b/.pipelines/scripts/verify_shell.sh @@ -30,6 +30,7 @@ filesToCheck=$(find . -type f -name "*.sh" -not -path './pkg/agent/testdata/*' - # Known bash-only scripts that intentionally use bash specific syntax. BASH_ONLY_LIST=$(cat <<'EOF' ./vhdbuilder/packer/install-ig.sh +./vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh ./parts/linux/cloud-init/artifacts/aks-localdns-hosts-setup.sh EOF ) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index eacf1678016..7069f8d5ed7 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,6 +97,7 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -117,6 +118,7 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) diff --git a/e2e/scenario/vmss_test.go b/e2e/scenario/vmss_test.go new file mode 100644 index 00000000000..23710d8a99a --- /dev/null +++ b/e2e/scenario/vmss_test.go @@ -0,0 +1,482 @@ +package scenario + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + "testing/synctest" + "time" + + "github.com/Azure/agentbaker/e2e/config" + "github.com/Azure/agentbaker/e2e/logging" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v7" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v8" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +type vmssCreationTestPolicy func(*http.Request) *http.Response + +type vmssCreationTestLogger struct { + *testing.T + messages []string +} + +func (l *vmssCreationTestLogger) Logf(format string, args ...any) { + l.messages = append(l.messages, fmt.Sprintf(format, args...)) + l.T.Logf(format, args...) +} + +func (f vmssCreationTestPolicy) Do(req *policy.Request) (*http.Response, error) { + return f(req.Raw()), nil +} + +type vmssCreationTestCase struct { + name string + provisionCode string + running bool + sshFails bool + skipSSH bool + viewFails bool + noVM bool + noNetwork bool + listFails bool + noNIC bool + vmAfterPoll bool + pendingPolls int + pollCount int + wantErr string + wantSSH bool + polled bool +} + +func TestCreateVMSSProvisioningErrorPrecedence(t *testing.T) { + for _, tt := range []vmssCreationTestCase{ + {name: "allocation failed", provisionCode: "AllocationFailed", sshFails: true}, + {name: "allocation failed with running guest and SSH failure", provisionCode: "AllocationFailed", running: true, sshFails: true, wantSSH: true}, + {name: "allocation failed without VM", provisionCode: "AllocationFailed", noVM: true}, + {name: "allocation failed without network profile", provisionCode: "AllocationFailed", noNetwork: true}, + {name: "allocation failed while instance listing fails", provisionCode: "AllocationFailed", listFails: true}, + {name: "allocation failed without NIC", provisionCode: "AllocationFailed", noNIC: true}, + {name: "allocation fails after pending without VM", provisionCode: "AllocationFailed", noVM: true, pendingPolls: 2}, + {name: "VM appears while creation is pending", running: true, vmAfterPoll: true, pendingPolls: 2, wantSSH: true}, + {name: "creation completes before VM appears", running: true, vmAfterPoll: true, wantSSH: true}, + {name: "deadline while creation is pending without VM", noVM: true, pendingPolls: 100, wantErr: "timeout waiting for VMSS VM"}, + {name: "successful creation without NIC", noNIC: true, wantErr: "no network interfaces found"}, + {name: "wrapped allocation failed", provisionCode: "ResourceOperationFailure", sshFails: true}, + {name: "OS provisioning failed without running guest", provisionCode: "OSProvisioningTimedOut", sshFails: true}, + {name: "CSE failed with running guest and SSH failure", provisionCode: "VMExtensionProvisioningError", running: true, sshFails: true, wantSSH: true}, + {name: "CSE failed with guest diagnostics available", provisionCode: "VMExtensionProvisioningError", running: true, wantSSH: true}, + {name: "CSE failed with SSH validation disabled", provisionCode: "VMExtensionProvisioningError", running: true, skipSSH: true}, + {name: "instance view unavailable", provisionCode: "AllocationFailed", viewFails: true, sshFails: true}, + {name: "creation succeeded but SSH failed", running: true, sshFails: true, wantSSH: true}, + {name: "creation and SSH succeeded", running: true, wantSSH: true}, + {name: "network available before creation completes", running: true, pendingPolls: 2, wantSSH: true}, + } { + t.Run(tt.name, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + previousAzure := config.Azure + config.Azure = tt.client(t) + t.Cleanup(func() { config.Azure = previousAzure }) + s := &Scenario{ + Config: Config{SkipSSHConnectivityValidation: tt.skipSSH}, + Runtime: &ScenarioRuntime{ + VMSSName: "vmss", + Cluster: &Cluster{Model: &armcontainerservice.ManagedCluster{ + Location: to.Ptr("southeastasia"), + Properties: &armcontainerservice.ManagedClusterProperties{ + NodeResourceGroup: to.Ptr("rg"), + }, + }}, + }, + } + sshErr := errors.New("SSH handshake failed") + sshClient := &SSHClient{} + sshCalled := false + logger := &vmssCreationTestLogger{T: t} + dialSSH := func(_ context.Context, _ *Bastion, ip string, _ []byte) (*SSHClient, error) { + require.True(t, tt.polled) + require.Equal(t, "10.0.0.4", ip) + if tt.provisionCode != "" { + require.Contains(t, strings.Join(logger.messages, "\n"), "VMSS vmss provisioning failed:") + } + sshCalled = true + if tt.sshFails { + return nil, sshErr + } + return sshClient, nil + } + ctx, cancel := context.WithTimeout(t.Context(), time.Minute) + defer cancel() + vm, err := createVMSS(logging.WithLogger(ctx, logger), s, "rg", armcompute.VirtualMachineScaleSet{}, dialSSH) + require.NotNil(t, vm) + assert.Equal(t, tt.wantSSH, sshCalled) + if tt.provisionCode != "" { + var responseErr *azcore.ResponseError + require.ErrorAs(t, err, &responseErr, "provisioning error must survive discovery or SSH failure") + assert.Equal(t, tt.provisionCode, responseErr.ErrorCode) + assert.Equal(t, tt.provisionCode == "AllocationFailed", isRetryableVMSSCreationError(fmt.Errorf("create VMSS: %w", err))) + assert.NotContains(t, strings.Join(logger.messages, "\n"), "after creation") + } else if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + } else if !tt.sshFails { + require.NoError(t, err) + require.NotNil(t, vm.VMSS) + } + require.True(t, tt.polled, "must exercise the actual SDK provisioning result") + if tt.wantSSH && tt.sshFails { + assert.ErrorIs(t, err, sshErr) + assert.ErrorContains(t, err, "failed to start bastion tunnel:") + if tt.provisionCode == "" { + assert.False(t, isRetryableVMSSCreationError(err)) + } + } + if tt.wantSSH && !tt.sshFails { + assert.Same(t, sshClient, vm.SSHClient, "retain the connection for guest diagnostics") + } + }) + }) + } +} + +func TestVMSSProvisioningErrorClassification(t *testing.T) { + oldSkip := config.Config.SkipTestsWithSKUCapacityIssue + t.Cleanup(func() { config.Config.SkipTestsWithSKUCapacityIssue = oldSkip }) + for _, tc := range []struct { + code string + status int + message string + wantRetry bool + wantSkip bool + }{ + {code: "AllocationFailed", status: 200, wantRetry: true}, + {code: "GalleryImageNotFound", status: 404, wantRetry: true}, + {code: "SkuNotAvailable", status: 409, wantSkip: true}, + {code: "OperationNotAllowed", status: 409, message: "exceeding approved quota", wantSkip: true}, + {code: "OperationNotAllowed", status: 409, message: "another operation is pending"}, + {code: "VMExtensionProvisioningError", status: 200}, + {code: "AllocationFailed", status: 500}, + {code: "GalleryImageNotFound", status: 500}, + {code: "SkuNotAvailable", status: 500}, + {code: "OperationNotAllowed", status: 500, message: "exceeding approved quota"}, + } { + t.Run(fmt.Sprintf("%s/%d/%s", tc.code, tc.status, tc.message), func(t *testing.T) { + armErr := &azcore.ResponseError{ + StatusCode: tc.status, + ErrorCode: tc.code, + RawResponse: &http.Response{ + StatusCode: tc.status, + Header: http.Header{"Content-Type": {"application/json"}}, + Body: io.NopCloser(strings.NewReader(fmt.Sprintf(`{"error":{"code":%q,"message":%q}}`, tc.code, tc.message))), + }, + } + for _, sshErr := range []error{nil, context.DeadlineExceeded} { + var err error = armErr + if sshErr != nil { + err = errors.Join(err, fmt.Errorf("failed to start bastion tunnel: %w", sshErr)) + } + err = fmt.Errorf("create VMSS: %w", err) + var responseErr *azcore.ResponseError + require.ErrorAs(t, err, &responseErr) + require.Same(t, armErr, responseErr) + require.Equal(t, tc.wantRetry, isRetryableVMSSCreationError(err)) + for _, skipEnabled := range []bool{false, true} { + config.Config.SkipTestsWithSKUCapacityIssue = skipEnabled + skipErr := skipIfSKUNotAvailableErr(err) + if tc.wantSkip && skipEnabled { + var skipped *skipError + require.ErrorAs(t, skipErr, &skipped) + } else { + require.NoError(t, skipErr) + } + } + } + }) + } + for _, err := range []error{nil, fmt.Errorf("failed to start bastion tunnel: %w", context.DeadlineExceeded)} { + require.False(t, isRetryableVMSSCreationError(err)) + for _, enabled := range []bool{false, true} { + config.Config.SkipTestsWithSKUCapacityIssue = enabled + require.NoError(t, skipIfSKUNotAvailableErr(err)) + } + } +} + +func (tt *vmssCreationTestCase) client(t *testing.T) *config.AzureClient { + t.Helper() + respond := vmssCreationTestPolicy(func(req *http.Request) *http.Response { return tt.respond(t, req) }) + options := &arm.ClientOptions{ClientOptions: policy.ClientOptions{ + PerCallPolicies: []policy.Policy{respond}, + }} + client := &config.AzureClient{} + var err error + client.VMSS, err = armcompute.NewVirtualMachineScaleSetsClient("test", nil, options) + require.NoError(t, err) + client.VMSSVM, err = armcompute.NewVirtualMachineScaleSetVMsClient("test", nil, options) + require.NoError(t, err) + client.NetworkInterfaces, err = armnetwork.NewInterfacesClient("test", nil, options) + require.NoError(t, err) + return client +} + +func (tt *vmssCreationTestCase) respond(t *testing.T, req *http.Request) *http.Response { + t.Helper() + const vmssPath = "/subscriptions/test/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/vmss" + const vmPath = vmssPath + "/virtualMachines/0" + vmBody := func(running bool) string { + powerState := "deallocated" + if running { + powerState = "running" + } + return fmt.Sprintf(`{"id":%q,"instanceId":"0","properties":{"networkProfile":{}, + "instanceView":{"statuses":[{"code":"PowerState/%s"}]}}}`, vmPath, powerState) + } + status := http.StatusOK + header := http.Header{"Content-Type": {"application/json"}} + var body string + switch { + case req.Method == http.MethodPut && req.URL.Path == vmssPath: + status = http.StatusCreated + header.Set("Azure-AsyncOperation", "https://management.azure.com/operations/create") + body = `{"properties":{"provisioningState":"Creating"}}` + case req.Method == http.MethodGet && req.URL.Path == "/operations/create": + tt.polled = true + tt.pollCount++ + body = `{"status":"Succeeded"}` + if tt.provisionCode != "" { + details := "" + if tt.provisionCode == "ResourceOperationFailure" { + details = `,"details":[{"code":"AllocationFailed","message":"insufficient capacity"}]` + } + body = fmt.Sprintf(`{"status":"Failed","error":{"code":%q,"message":"provisioning failed", + "target":"vmss"%s}}`, tt.provisionCode, details) + } + if tt.pollCount <= tt.pendingPolls { + body = `{"status":"InProgress"}` + } + case req.Method == http.MethodGet && req.URL.Path == vmssPath: + body = fmt.Sprintf(`{"id":%q,"properties":{"provisioningState":"Succeeded"}}`, vmssPath) + case req.Method == http.MethodGet && req.URL.Path == vmssPath+"/virtualMachines": + body = `{"value":[` + vmBody(true) + `]}` + if tt.noVM || (tt.vmAfterPoll && !tt.polled) { + body = `{"value":[]}` + } + if tt.noNetwork { + body = fmt.Sprintf(`{"value":[{"id":%q,"instanceId":"0","properties":{}}]}`, vmPath) + } + if tt.listFails { + status = http.StatusNotFound + body = `{"error":{"code":"ResourceNotFound","message":"VMSS not found"}}` + } + case req.Method == http.MethodGet && strings.EqualFold(req.URL.Path, vmPath+"/networkInterfaces"): + if !tt.vmAfterPoll { + require.False(t, tt.polled, "discover available networking before waiting for creation") + } + body = `{"value":[{"properties":{"ipConfigurations":[{"properties":{"privateIPAddress":"10.0.0.4"}}]}}]}` + if tt.noNIC { + body = `{"value":[]}` + } + case req.Method == http.MethodGet && req.URL.Path == vmPath: + require.True(t, tt.polled) + require.Equal(t, "instanceView", req.URL.Query().Get("$expand")) + body = vmBody(tt.running) + if tt.viewFails { + status = http.StatusNotFound + body = `{"error":{"code":"ResourceNotFound","message":"VM not found"}}` + } + default: + t.Fatalf("unexpected Azure request: %s %s", req.Method, req.URL) + } + return &http.Response{ + StatusCode: status, Header: header, Body: io.NopCloser(strings.NewReader(body)), Request: req, + } +} + +func TestWriteScriptHotfixFixture(t *testing.T) { + buildDir := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(buildDir, "generated"), 0o755)) + fixture := ScriptHotfixFixture{ + Platform: "ubuntu", + Files: []ScriptHotfixFile{{ + Destination: "/opt/azure/containers/provision_configs.sh", + Mode: "0744", + Payload: []byte("#!/bin/bash\necho e2e\n"), + }}, + } + + require.NoError(t, writeScriptHotfixFixture(buildDir, fixture)) + entries, err := os.ReadDir(filepath.Join(buildDir, "generated")) + require.NoError(t, err) + require.Len(t, entries, 1) + require.Equal(t, "rendered_nodecustomdata_ubuntu.yml", entries[0].Name()) + + renderedData, err := os.ReadFile(filepath.Join( + buildDir, + "generated", + "rendered_nodecustomdata_ubuntu.yml", + )) + require.NoError(t, err) + var rendered scriptHotfixFixtureNodeCustomData + require.NoError(t, yaml.Unmarshal(renderedData, &rendered)) + require.Len(t, rendered.WriteFiles, 1) + require.Equal(t, fixture.Files[0].Destination, rendered.WriteFiles[0].Path) + require.Equal(t, fixture.Files[0].Mode, rendered.WriteFiles[0].Permissions) + require.Equal(t, "base64", rendered.WriteFiles[0].Encoding) + payload, err := base64.StdEncoding.DecodeString(rendered.WriteFiles[0].Content) + require.NoError(t, err) + require.Equal(t, fixture.Files[0].Payload, payload) +} + +func TestWriteScriptHotfixFixtureRejectsInvalidData(t *testing.T) { + validFixture := func() ScriptHotfixFixture { + return ScriptHotfixFixture{ + Platform: "ubuntu", + Files: []ScriptHotfixFile{{ + Destination: "/opt/azure/containers/provision_configs.sh", + Mode: "0744", + Payload: []byte("#!/bin/bash\n"), + }}, + } + } + tests := []struct { + name string + mutate func(*ScriptHotfixFixture) + }{ + { + name: "relative destination", + mutate: func(fixture *ScriptHotfixFixture) { + fixture.Files[0].Destination = "opt/provision_configs.sh" + }, + }, + { + name: "invalid mode", + mutate: func(fixture *ScriptHotfixFixture) { + fixture.Files[0].Mode = "0999" + }, + }, + { + name: "unsupported platform", + mutate: func(fixture *ScriptHotfixFixture) { + fixture.Platform = "other" + }, + }, + { + name: "empty payload", + mutate: func(fixture *ScriptHotfixFixture) { + fixture.Files[0].Payload = nil + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fixture := validFixture() + test.mutate(&fixture) + buildDir := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(buildDir, "generated"), 0o755)) + require.Error(t, writeScriptHotfixFixture(buildDir, fixture)) + }) + } +} + +// TestCSEExitCodeOutboundConnFail pins the exit code constant to the value emitted by +// ERR_OUTBOUND_CONN_FAIL in parts/linux/cloud-init/artifacts/cse_helpers.sh. If the +// product error code changes, this test forces the harness mitigation to be updated. +func TestGetBaseVMSSModelUsesScenarioVMSize(t *testing.T) { + s := &Scenario{ + Runtime: &ScenarioRuntime{VMSize: "Standard_D2ds_v5"}, + } + assert.Equal(t, s.Runtime.VMSize, scenarioVMSize(s)) +} + +func TestCSEExitCodeOutboundConnFail(t *testing.T) { + require.Equal(t, "50", cseExitCodeOutboundConnFail) +} + +// TestParseLinuxCSEMessageOutboundExitCode verifies that parseLinuxCSEMessage extracts the +// outbound-connectivity exit code from a real CustomScript extension instance-view status. +// getLinuxCSEExitCode relies on this parsing to classify the retryable e2e flake, so a +// change to the message format must be reflected here. +func TestParseLinuxCSEMessageOutboundExitCode(t *testing.T) { + tests := []struct { + name string + code string + message string + wantExitCode string + wantErr bool + }{ + { + name: "well-formed CSE json with outbound exit code", + code: "ProvisioningState/failed/0", + message: `Enable failed: [stdout] { "ExitCode": "50", "Output": "+ exit 50" } [stderr]`, + wantExitCode: "50", + }, + { + name: "unparsable body falls back to extension exit status", + code: "ProvisioningState/failed/0", + message: `Enable failed: failed to execute command: command terminated with exit status=50 [stdout]not-json[stderr]`, + wantExitCode: "50", + }, + { + name: "well-formed CSE json with non-outbound exit code", + code: "ProvisioningState/failed/0", + message: `Enable failed: [stdout] { "ExitCode": "51", "Output": "+ exit 51" } [stderr]`, + wantExitCode: "51", + }, + { + // Real Ubuntu2204_HTTPSProxy_PrivateDNS/default failure: the outer extension + // wrapper and the CSE status both report 50. + name: "real outbound flake, outer exit 50 and cse exit 50", + code: "ProvisioningState/failed/0", + message: "failed to execute command: command terminated with exit status=50\n[stdout]\n" + + `{ "ExitCode": "50", "Output": "Processing manual pages under /usr/local/man...\n++ date\n+ echo 'man-db finished updates'\n+ exit 50", "Error": "", "ExecDuration": "155", "BootDatapoints": { "KubeletStartTime": "n/a" } }` + + "\n\n[stderr]\ndate: invalid date 'n/a'\n", + wantExitCode: "50", + }, + { + // Real Ubuntu2204_HTTPSProxy_PrivateDNS/scriptless_nbc failure: the outer + // extension wrapper reports exit status=1, but the CSE status reports 50. The + // classifier must read the CSE ExitCode field, not the outer wrapper. + name: "real outbound flake, outer exit 1 but cse exit 50", + code: "ProvisioningState/failed/0", + message: "failed to execute command: command terminated with exit status=1\n[stdout]\n" + + `{ "ExitCode": "50", "Output": "man-db finished updates\n+ exit 50", "Error": "", "ExecDuration": "70", "BootDatapoints": { "KubeletStartTime": "n/a" } }` + + "\n\n[stderr]\ndate\n", + wantExitCode: "50", + }, + { + name: "no parsable body", + code: "ProvisioningState/failed/0", + message: `Enable failed with no parsable body`, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + status := armcompute.InstanceViewStatus{ + Code: to.Ptr(tt.code), + Message: to.Ptr(tt.message), + } + cseStatus, err := parseLinuxCSEMessage(status) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotNil(t, cseStatus) + require.Equal(t, tt.wantExitCode, cseStatus.ExitCode) + }) + } +} diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh new file mode 100644 index 00000000000..2154496d832 --- /dev/null +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# shellcheck disable=SC1090,SC2329 + +Describe 'trim-2604-cvm-packages' + TRIM_SCRIPT="./vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh" + + setup_trim() { + TEST_DIR="$(mktemp -d)" + source "${TRIM_SCRIPT}" + MARKED_FOR_REMOVAL_PACKAGES_FILE="${TEST_DIR}/marked-for-removal-packages.txt" + REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" + } + + cleanup_trim() { + rm -rf "${TEST_DIR}" + } + + BeforeEach 'setup_trim' + AfterEach 'cleanup_trim' + + It 'filters comments and absent packages before purging' + printf '%s\n' '# comment' remove-me absent-package '' > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" + printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + + dpkg-query() { + for argument in "$@"; do + package="${argument}" + done + case "${package}" in + remove-me|required-package) + echo installed + ;; + *) + return 1 + ;; + esac + } + apt-get() { echo "apt-get $*"; } + apt-mark() { echo "apt-mark $*"; } + + When call main + The status should be success + The output should include "Purging 1 installed server-cvm packages marked for removal" + The output should include "apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential remove-me" + The output should not include "allow-remove-essential absent-package" + The output should include "apt-mark manual curl gpg jq logrotate rsyslog sudo xfsprogs" + End + + It 'fails when the removal list is empty' + : > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" + printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + + When call main + The status should be failure + The error should include "Marked-for-removal package list is missing or empty" + End + + It 'fails when a required package is missing after trimming' + printf '%s\n' absent-package > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" + printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + + dpkg-query() { return 1; } + apt-mark() { return 0; } + + When call main + The status should be failure + The output should include "No installed server-cvm packages marked for removal were found" + The error should include "Required CVM package pattern is not installed: required-package" + End + + It 'supports final verification without running another purge' + printf '%s\n' remove-me > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" + printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + + dpkg-query() { + echo installed + } + apt-get() { + echo "unexpected apt-get" + return 1 + } + apt-mark() { + echo "unexpected apt-mark" + return 1 + } + + When call main --verify-only + The status should be success + The output should not include "unexpected" + End + + It 'fails final verification when the required package list is empty' + : > "${REQUIRED_PACKAGES_FILE}" + + When call main --verify-only + The status should be failure + The error should include "Required package list is missing or empty" + End + + It 'rejects unknown arguments' + When call main --unknown + The status should be failure + The error should include "Unknown argument: --unknown" + End +End diff --git a/vhdbuilder/packer/post-install-dependencies.sh b/vhdbuilder/packer/post-install-dependencies.sh index 72509dae75c..898b2429435 100644 --- a/vhdbuilder/packer/post-install-dependencies.sh +++ b/vhdbuilder/packer/post-install-dependencies.sh @@ -76,6 +76,10 @@ if [ $OS = $UBUNTU_OS_NAME ]; then ua status detachAndCleanUpUA fi + + if [ "${UBUNTU_RELEASE}" = "26.04" ] && isMinimalImage && grep -q "cvm" <<< "$FEATURE_FLAGS"; then + /bin/bash /home/packer/trim-2604-cvm-packages.sh --verify-only || exit 1 + fi capture_benchmark "${SCRIPT_NAME}_log_and_detach_ua" fi diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 3d5df1fd28b..61fe9be4239 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -2,7 +2,6 @@ OS=$(sort -r /etc/*-release | sed -n 's/^ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') OS_VERSION=$(sort -r /etc/*-release | sed -n 's/^VERSION_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') OS_VARIANT=$(sort -r /etc/*-release | sed -n 's/^VARIANT_ID=//p' | head -n1 | tr -d '"' | tr '[:lower:]' '[:upper:]') - THIS_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" #the following sed removes all comments of the format {{/* */}} @@ -22,7 +21,6 @@ CPU_ARCH=$(getCPUArch) #amd64 or arm64 VHD_LOGS_FILEPATH=/opt/azure/vhd-install.complete COMPONENTS_FILEPATH=/opt/azure/components.json PERFORMANCE_DATA_FILE=/opt/azure/vhd-build-performance-data.json - #this is used by post build test to check whether the compoenents do indeed exist cat components.json > ${COMPONENTS_FILEPATH} echo "Starting build on " $(date) > ${VHD_LOGS_FILEPATH} diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 96b91f814b2..1fe115421e5 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -198,7 +198,6 @@ libxmuu1 libxslt1.1 linux-perf locales -logrotate lshw lsof lvm2 @@ -304,7 +303,6 @@ vim-tiny whiptail xauth xdg-user-dirs -xfsprogs xml-core xxd zerofree diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index f65fae02905..b9781bcf57d 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -21,6 +21,8 @@ linux-modules-*-azure-fde linux-tools-*-azure linux-tools-*-azure-fde linux-tools-azure-fde +logrotate nullboot rsyslog sudo +xfsprogs diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 7306a0a5139..258a4f60d23 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -1,5 +1,4 @@ #!/bin/bash -set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" @@ -9,9 +8,21 @@ readPackageList() { sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "$1" } +validatePackageList() { + local package_file="$1" + local description="$2" + + if [ ! -s "${package_file}" ] || [ -z "$(readPackageList "${package_file}")" ]; then + echo "${description} is missing or empty: ${package_file}" >&2 + return 1 + fi +} + verifyRequiredPackagesInstalled() { local required + validatePackageList "${REQUIRED_PACKAGES_FILE}" "Required package list" || return 1 + while IFS= read -r required; do if ! dpkg-query -W -f='${db:Status-Status}\n' "${required}" 2>/dev/null | grep -Fxq "installed"; then echo "Required CVM package pattern is not installed: ${required}" >&2 @@ -24,14 +35,20 @@ main() { local package local -a purge_packages=() - [ -s "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" ] || { - echo "Marked-for-removal package list is missing or empty: ${MARKED_FOR_REMOVAL_PACKAGES_FILE}" >&2 - return 1 - } - [ -s "${REQUIRED_PACKAGES_FILE}" ] || { - echo "Required package list is missing or empty: ${REQUIRED_PACKAGES_FILE}" >&2 - return 1 - } + case "${1:-}" in + --verify-only) + verifyRequiredPackagesInstalled + return + ;; + "") + ;; + *) + echo "Unknown argument: $1" >&2 + return 1 + ;; + esac + + validatePackageList "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" "Marked-for-removal package list" || return 1 while IFS= read -r package; do if [ "$(dpkg-query -W -f='${db:Status-Status}' "${package}" 2>/dev/null || true)" = "installed" ]; then @@ -46,8 +63,11 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual curl gpg jq rsyslog sudo + apt-mark manual curl gpg jq logrotate rsyslog sudo xfsprogs verifyRequiredPackagesInstalled } -main "$@" +if [ "${BASH_SOURCE[0]}" = "$0" ]; then + set -euo pipefail + main "$@" +fi From 40cccee892aae8441fb5792d2f55230c7f850a59 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 10:33:50 -0400 Subject: [PATCH 33/47] fix --- .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 1fe115421e5..6635696d90a 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -110,8 +110,6 @@ libgprofng0 libgstreamer1.0-0 libgudev-1.0-0 libibverbs1 -libicu78 -libinih1 libintl-perl libintl-xs-perl libip4tc2 @@ -180,7 +178,6 @@ libtracefs1 libuchardet0 libudisks2-0 libunwind8 -liburcu8t64 libuv1t64 libvolume-key1 libx11-6 From 9b0a880a9e814a5773f6e33a9ad1963f43128760 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 10:57:00 -0400 Subject: [PATCH 34/47] fix --- .pipelines/templates/.builder-release-template.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 7069f8d5ed7..eacf1678016 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,7 +97,6 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -118,7 +117,6 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) From f681ef545405a5c7ecf5e5d4a8f07dab8e6b2be8 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 13:03:08 -0400 Subject: [PATCH 35/47] fix --- .../packer/trim_2604_cvm_packages_spec.sh | 11 +++++--- vhdbuilder/packer/pre-install-dependencies.sh | 7 ++--- .../final-required-packages.txt | 26 +++++++++++++++++++ .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 10 ++++--- vhdbuilder/packer/vhd-image-builder-cvm.json | 5 ++++ 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh index 2154496d832..d6d4d5b0391 100644 --- a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -10,6 +10,7 @@ Describe 'trim-2604-cvm-packages' source "${TRIM_SCRIPT}" MARKED_FOR_REMOVAL_PACKAGES_FILE="${TEST_DIR}/marked-for-removal-packages.txt" REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" + FINAL_REQUIRED_PACKAGES_FILE="${TEST_DIR}/final-required-packages.txt" } cleanup_trim() { @@ -71,10 +72,14 @@ Describe 'trim-2604-cvm-packages' It 'supports final verification without running another purge' printf '%s\n' remove-me > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" - printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + printf '%s\n' initial-required-package > "${REQUIRED_PACKAGES_FILE}" + printf '%s\n' final-required-package > "${FINAL_REQUIRED_PACKAGES_FILE}" dpkg-query() { - echo installed + for argument in "$@"; do + package="${argument}" + done + [ "${package}" = "final-required-package" ] && echo installed } apt-get() { echo "unexpected apt-get" @@ -91,7 +96,7 @@ Describe 'trim-2604-cvm-packages' End It 'fails final verification when the required package list is empty' - : > "${REQUIRED_PACKAGES_FILE}" + : > "${FINAL_REQUIRED_PACKAGES_FILE}" When call main --verify-only The status should be failure diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 61fe9be4239..9576205a464 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -184,12 +184,9 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then # Choose kernel packages based on Ubuntu version and architecture if grep -q "cvm" <<< "$FEATURE_FLAGS"; then - KERNEL_IMAGE="linux-image-azure-fde-lts-${UBUNTU_RELEASE}" + KERNEL_IMAGE="linux-azure-fde-lts-${UBUNTU_RELEASE}" KERNEL_PACKAGES=( - "linux-image-azure-fde-lts-${UBUNTU_RELEASE}" - "linux-tools-azure-lts-${UBUNTU_RELEASE}" - "linux-cloud-tools-azure-lts-${UBUNTU_RELEASE}" - "linux-headers-azure-lts-${UBUNTU_RELEASE}" + "${KERNEL_IMAGE}" ) echo "Installing fde LTS kernel for CVM Ubuntu ${UBUNTU_RELEASE}" else diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt new file mode 100644 index 00000000000..984ad9bf3ef --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt @@ -0,0 +1,26 @@ +cryptsetup +cryptsetup-bin +curl +gpg +jq +libcryptsetup12 +lsscsi +linux-azure-fde-lts-26.04 +linux-azure-fde-cloud-tools-* +linux-azure-fde-headers-* +linux-azure-fde-tools-* +linux-cloud-tools-*-azure-fde +linux-cloud-tools-azure-fde-lts-26.04 +linux-headers-*-azure-fde +linux-headers-azure-fde-lts-26.04 +linux-image-*-azure-fde +linux-image-azure-fde-lts-26.04 +linux-main-modules-zfs-*-azure-fde +linux-modules-*-azure-fde +linux-tools-*-azure-fde +linux-tools-azure-fde-lts-26.04 +logrotate +nullboot +rsyslog +sudo +xfsprogs diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 258a4f60d23..4b6340e3708 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -3,6 +3,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" +FINAL_REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/final-required-packages.txt" readPackageList() { sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "$1" @@ -19,16 +20,17 @@ validatePackageList() { } verifyRequiredPackagesInstalled() { + local package_file="$1" local required - validatePackageList "${REQUIRED_PACKAGES_FILE}" "Required package list" || return 1 + validatePackageList "${package_file}" "Required package list" || return 1 while IFS= read -r required; do if ! dpkg-query -W -f='${db:Status-Status}\n' "${required}" 2>/dev/null | grep -Fxq "installed"; then echo "Required CVM package pattern is not installed: ${required}" >&2 return 1 fi - done < <(readPackageList "${REQUIRED_PACKAGES_FILE}") + done < <(readPackageList "${package_file}") } main() { @@ -37,7 +39,7 @@ main() { case "${1:-}" in --verify-only) - verifyRequiredPackagesInstalled + verifyRequiredPackagesInstalled "${FINAL_REQUIRED_PACKAGES_FILE}" return ;; "") @@ -64,7 +66,7 @@ main() { fi apt-mark manual curl gpg jq logrotate rsyslog sudo xfsprogs - verifyRequiredPackagesInstalled + verifyRequiredPackagesInstalled "${REQUIRED_PACKAGES_FILE}" } if [ "${BASH_SOURCE[0]}" = "$0" ]; then diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 6f10d669991..64e50fee5fb 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -399,6 +399,11 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", "destination": "/home/packer/required-packages.txt" }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt", + "destination": "/home/packer/final-required-packages.txt" + }, { "type": "file", "source": "vhdbuilder/packer/install-ig.sh", From 6150ab05a010cac2e31c660628b926f8fbd58c1c Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 15:33:52 -0400 Subject: [PATCH 36/47] fix --- vhdbuilder/packer/pre-install-dependencies.sh | 3 ++- vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/vhdbuilder/packer/pre-install-dependencies.sh b/vhdbuilder/packer/pre-install-dependencies.sh index 9576205a464..b9570e3db95 100644 --- a/vhdbuilder/packer/pre-install-dependencies.sh +++ b/vhdbuilder/packer/pre-install-dependencies.sh @@ -188,6 +188,7 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then KERNEL_PACKAGES=( "${KERNEL_IMAGE}" ) + MODULES_EXTRA_PKG="linux-modules-extra-azure-fde-lts-${UBUNTU_RELEASE}" echo "Installing fde LTS kernel for CVM Ubuntu ${UBUNTU_RELEASE}" else # Use LTS kernel for other versions @@ -198,11 +199,11 @@ if [[ ${UBUNTU_RELEASE//./} -ge 2204 && "${ENABLE_FIPS,,}" != "true" ]]; then "linux-cloud-tools-azure-lts-${UBUNTU_RELEASE}" "linux-headers-azure-lts-${UBUNTU_RELEASE}" ) + MODULES_EXTRA_PKG="linux-modules-extra-azure-lts-${UBUNTU_RELEASE}" echo "Installing LTS kernel for Ubuntu ${UBUNTU_RELEASE}" fi # Add modules-extra only when the package exists in the current apt repo - MODULES_EXTRA_PKG="linux-modules-extra-azure-lts-${UBUNTU_RELEASE}" if apt-cache show "${MODULES_EXTRA_PKG}" &>/dev/null; then KERNEL_PACKAGES+=("${MODULES_EXTRA_PKG}") else diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index b9781bcf57d..f6651a4ee4d 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -9,7 +9,6 @@ linux-azure-fde linux-azure-fde-cloud-tools-* linux-azure-fde-headers-* linux-azure-fde-tools-* -linux-azure-tools-* linux-cloud-tools-*-azure-fde linux-cloud-tools-azure-fde linux-headers-*-azure-fde @@ -18,7 +17,6 @@ linux-image-*-azure-fde linux-image-azure-fde linux-main-modules-zfs-*-azure-fde linux-modules-*-azure-fde -linux-tools-*-azure linux-tools-*-azure-fde linux-tools-azure-fde logrotate From e0a865031305889a17ec163ed4e4d4b865926dd1 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 15:42:50 -0400 Subject: [PATCH 37/47] fix --- .../packer/trim_2604_cvm_packages_spec.sh | 35 +++++++++++++++++++ .../final-forbidden-packages.txt | 23 ++++++++++++ .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 18 +++++++++- vhdbuilder/packer/vhd-image-builder-cvm.json | 5 +++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh index d6d4d5b0391..ceabe06b1a8 100644 --- a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -11,6 +11,7 @@ Describe 'trim-2604-cvm-packages' MARKED_FOR_REMOVAL_PACKAGES_FILE="${TEST_DIR}/marked-for-removal-packages.txt" REQUIRED_PACKAGES_FILE="${TEST_DIR}/required-packages.txt" FINAL_REQUIRED_PACKAGES_FILE="${TEST_DIR}/final-required-packages.txt" + FINAL_FORBIDDEN_PACKAGES_FILE="${TEST_DIR}/final-forbidden-packages.txt" } cleanup_trim() { @@ -74,6 +75,7 @@ Describe 'trim-2604-cvm-packages' printf '%s\n' remove-me > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" printf '%s\n' initial-required-package > "${REQUIRED_PACKAGES_FILE}" printf '%s\n' final-required-package > "${FINAL_REQUIRED_PACKAGES_FILE}" + printf '%s\n' forbidden-package > "${FINAL_FORBIDDEN_PACKAGES_FILE}" dpkg-query() { for argument in "$@"; do @@ -95,6 +97,26 @@ Describe 'trim-2604-cvm-packages' The output should not include "unexpected" End + It 'fails final verification when a forbidden vanilla package is installed' + printf '%s\n' final-required-package > "${FINAL_REQUIRED_PACKAGES_FILE}" + printf '%s\n' vanilla-package > "${FINAL_FORBIDDEN_PACKAGES_FILE}" + + dpkg-query() { + for argument in "$@"; do + package="${argument}" + done + case "${package}" in + final-required-package|vanilla-package) + echo installed + ;; + esac + } + + When call main --verify-only + The status should be failure + The error should include "Forbidden CVM package pattern is installed: vanilla-package" + End + It 'fails final verification when the required package list is empty' : > "${FINAL_REQUIRED_PACKAGES_FILE}" @@ -103,6 +125,19 @@ Describe 'trim-2604-cvm-packages' The error should include "Required package list is missing or empty" End + It 'fails final verification when the forbidden package list is empty' + printf '%s\n' final-required-package > "${FINAL_REQUIRED_PACKAGES_FILE}" + : > "${FINAL_FORBIDDEN_PACKAGES_FILE}" + + dpkg-query() { + echo installed + } + + When call main --verify-only + The status should be failure + The error should include "Forbidden package list is missing or empty" + End + It 'rejects unknown arguments' When call main --unknown The status should be failure diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt new file mode 100644 index 00000000000..bca8fdd364a --- /dev/null +++ b/vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt @@ -0,0 +1,23 @@ +linux-azure +linux-azure-lts-* +linux-azure-cloud-tools-* +linux-azure-headers-* +linux-azure-tools-* +linux-cloud-tools-*-azure +linux-cloud-tools-azure +linux-cloud-tools-azure-lts-* +linux-headers-*-azure +linux-headers-azure +linux-headers-azure-lts-* +linux-image-*-azure +linux-image-azure +linux-image-azure-lts-* +linux-modules-*-azure +linux-modules-azure +linux-modules-azure-lts-* +linux-modules-extra-*-azure +linux-modules-extra-azure +linux-modules-extra-azure-lts-* +linux-tools-*-azure +linux-tools-azure +linux-tools-azure-lts-* diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 4b6340e3708..15db8417a18 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -4,6 +4,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" FINAL_REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/final-required-packages.txt" +FINAL_FORBIDDEN_PACKAGES_FILE="${SCRIPT_DIR}/final-forbidden-packages.txt" readPackageList() { sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "$1" @@ -33,13 +34,28 @@ verifyRequiredPackagesInstalled() { done < <(readPackageList "${package_file}") } +verifyForbiddenPackagesAbsent() { + local package_file="$1" + local forbidden + + validatePackageList "${package_file}" "Forbidden package list" || return 1 + + while IFS= read -r forbidden; do + if dpkg-query -W -f='${db:Status-Status}\n' "${forbidden}" 2>/dev/null | grep -Fxq "installed"; then + echo "Forbidden CVM package pattern is installed: ${forbidden}" >&2 + return 1 + fi + done < <(readPackageList "${package_file}") +} + main() { local package local -a purge_packages=() case "${1:-}" in --verify-only) - verifyRequiredPackagesInstalled "${FINAL_REQUIRED_PACKAGES_FILE}" + verifyRequiredPackagesInstalled "${FINAL_REQUIRED_PACKAGES_FILE}" || return 1 + verifyForbiddenPackagesAbsent "${FINAL_FORBIDDEN_PACKAGES_FILE}" return ;; "") diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 64e50fee5fb..6780df5b5ee 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -404,6 +404,11 @@ "source": "vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt", "destination": "/home/packer/final-required-packages.txt" }, + { + "type": "file", + "source": "vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt", + "destination": "/home/packer/final-forbidden-packages.txt" + }, { "type": "file", "source": "vhdbuilder/packer/install-ig.sh", From ca6cbd5937e6ed35abb5857c808b503817c0439b Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 16:19:22 -0400 Subject: [PATCH 38/47] fix --- .pipelines/templates/.builder-release-template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index eacf1678016..7069f8d5ed7 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,6 +97,7 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -117,6 +118,7 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) From 7e1a57d236c3ba353c973b15338db52ec94190e8 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 16:27:47 -0400 Subject: [PATCH 39/47] fix --- spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh | 2 +- vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt | 2 ++ .../packer/ubuntu-2604-cvm/marked-for-removal-packages.txt | 3 --- vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt | 2 ++ vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh index ceabe06b1a8..4c070117fa6 100644 --- a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -46,7 +46,7 @@ Describe 'trim-2604-cvm-packages' The output should include "Purging 1 installed server-cvm packages marked for removal" The output should include "apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential remove-me" The output should not include "allow-remove-essential absent-package" - The output should include "apt-mark manual curl gpg jq logrotate rsyslog sudo xfsprogs" + The output should include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo tcpdump xfsprogs" End It 'fails when the removal list is empty' diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt index 984ad9bf3ef..90dec9e6726 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt @@ -1,5 +1,6 @@ cryptsetup cryptsetup-bin +cron curl gpg jq @@ -23,4 +24,5 @@ logrotate nullboot rsyslog sudo +tcpdump xfsprogs diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt index 6635696d90a..a9d5b9b5678 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt @@ -21,8 +21,6 @@ cloud-initramfs-copymods cloud-initramfs-dyn-netconf command-not-found cpio -cron -cron-daemon-common debconf-i18n dmeventd dosfstools @@ -274,7 +272,6 @@ systemd-cryptsetup systemd-hwe-hwdb tcl tcl8.6 -tcpdump telnet thin-provisioning-tools time diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt index f6651a4ee4d..5c346fe2b0c 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt @@ -1,5 +1,6 @@ cryptsetup cryptsetup-bin +cron curl gpg jq @@ -23,4 +24,5 @@ logrotate nullboot rsyslog sudo +tcpdump xfsprogs diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 15db8417a18..1d66b6b85d4 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -81,7 +81,7 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual curl gpg jq logrotate rsyslog sudo xfsprogs + apt-mark manual cron curl gpg jq logrotate rsyslog sudo tcpdump xfsprogs verifyRequiredPackagesInstalled "${REQUIRED_PACKAGES_FILE}" } From 7b85cf940221b44439da7b918d0a882269a1b05d Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 16:42:39 -0400 Subject: [PATCH 40/47] fix --- ...604-minimal-cvm-final-forbidden-packages.txt} | 0 ...2604-minimal-cvm-final-required-packages.txt} | 0 ...-minimal-cvm-marked-for-removal-packages.txt} | 0 ...xt => 2604-minimal-cvm-required-packages.txt} | 0 .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 8 ++++---- vhdbuilder/packer/vhd-image-builder-cvm.json | 16 ++++++++-------- 6 files changed, 12 insertions(+), 12 deletions(-) rename vhdbuilder/packer/ubuntu-2604-cvm/{final-forbidden-packages.txt => 2604-minimal-cvm-final-forbidden-packages.txt} (100%) rename vhdbuilder/packer/ubuntu-2604-cvm/{final-required-packages.txt => 2604-minimal-cvm-final-required-packages.txt} (100%) rename vhdbuilder/packer/ubuntu-2604-cvm/{marked-for-removal-packages.txt => 2604-minimal-cvm-marked-for-removal-packages.txt} (100%) rename vhdbuilder/packer/ubuntu-2604-cvm/{required-packages.txt => 2604-minimal-cvm-required-packages.txt} (100%) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-forbidden-packages.txt similarity index 100% rename from vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt rename to vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-forbidden-packages.txt diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt similarity index 100% rename from vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt rename to vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt similarity index 100% rename from vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt rename to vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt similarity index 100% rename from vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt rename to vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 1d66b6b85d4..4e498419c60 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -1,10 +1,10 @@ #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/marked-for-removal-packages.txt" -REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/required-packages.txt" -FINAL_REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/final-required-packages.txt" -FINAL_FORBIDDEN_PACKAGES_FILE="${SCRIPT_DIR}/final-forbidden-packages.txt" +MARKED_FOR_REMOVAL_PACKAGES_FILE="${SCRIPT_DIR}/2604-minimal-cvm-marked-for-removal-packages.txt" +REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/2604-minimal-cvm-required-packages.txt" +FINAL_REQUIRED_PACKAGES_FILE="${SCRIPT_DIR}/2604-minimal-cvm-final-required-packages.txt" +FINAL_FORBIDDEN_PACKAGES_FILE="${SCRIPT_DIR}/2604-minimal-cvm-final-forbidden-packages.txt" readPackageList() { sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' "$1" diff --git a/vhdbuilder/packer/vhd-image-builder-cvm.json b/vhdbuilder/packer/vhd-image-builder-cvm.json index 6780df5b5ee..33b0f1b57a6 100644 --- a/vhdbuilder/packer/vhd-image-builder-cvm.json +++ b/vhdbuilder/packer/vhd-image-builder-cvm.json @@ -391,23 +391,23 @@ }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/marked-for-removal-packages.txt", - "destination": "/home/packer/marked-for-removal-packages.txt" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt", + "destination": "/home/packer/2604-minimal-cvm-marked-for-removal-packages.txt" }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/required-packages.txt", - "destination": "/home/packer/required-packages.txt" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt", + "destination": "/home/packer/2604-minimal-cvm-required-packages.txt" }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/final-required-packages.txt", - "destination": "/home/packer/final-required-packages.txt" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt", + "destination": "/home/packer/2604-minimal-cvm-final-required-packages.txt" }, { "type": "file", - "source": "vhdbuilder/packer/ubuntu-2604-cvm/final-forbidden-packages.txt", - "destination": "/home/packer/final-forbidden-packages.txt" + "source": "vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-forbidden-packages.txt", + "destination": "/home/packer/2604-minimal-cvm-final-forbidden-packages.txt" }, { "type": "file", From dc51d9f8b77a89a0a87c5b2bf7d661356c38369e Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Thu, 17 Sep 2026 16:56:01 -0400 Subject: [PATCH 41/47] fix --- vhdbuilder/packer/test/linux-vhd-content-test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vhdbuilder/packer/test/linux-vhd-content-test.sh b/vhdbuilder/packer/test/linux-vhd-content-test.sh index 6a9b6c185cc..805f0461828 100644 --- a/vhdbuilder/packer/test/linux-vhd-content-test.sh +++ b/vhdbuilder/packer/test/linux-vhd-content-test.sh @@ -74,7 +74,10 @@ LOCAL_GIT_BRANCH=${GIT_BRANCH//\//-} SKIP_GIT_CLONE=false # Git is not present in the base image, so we need to install or bypass it. if [ "$OS_SKU" = "Ubuntu" ]; then - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y git + if ! sudo DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 install -y git; then + err 'git-install' "Failed to install git" + exit 1 + fi elif [ "$OS_SKU" = "Flatcar" ] || [ "$OS_SKU" = "AzureContainerLinux" ]; then : # Flatcar/ACL comes with git pre-installed elif [ "$OS_SKU" = "AzureLinuxOSGuard" ]; then From 02360bd81a15779860eba1f12e5b5b4393acbd1c Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 09:27:17 -0400 Subject: [PATCH 42/47] fix --- .../ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt | 1 - .../ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt index 90dec9e6726..577d9a724e7 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-final-required-packages.txt @@ -24,5 +24,4 @@ logrotate nullboot rsyslog sudo -tcpdump xfsprogs diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt index 5c346fe2b0c..c4fce46770c 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-required-packages.txt @@ -24,5 +24,4 @@ logrotate nullboot rsyslog sudo -tcpdump xfsprogs From f35862caab22dde8eeafa4404b9c34df7034b542 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 09:43:53 -0400 Subject: [PATCH 43/47] fix --- spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh | 2 +- vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh index 4c070117fa6..24b3350e413 100644 --- a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -46,7 +46,7 @@ Describe 'trim-2604-cvm-packages' The output should include "Purging 1 installed server-cvm packages marked for removal" The output should include "apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential remove-me" The output should not include "allow-remove-essential absent-package" - The output should include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo tcpdump xfsprogs" + The output should include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs" End It 'fails when the removal list is empty' diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 4e498419c60..232c9c0f69d 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -81,7 +81,7 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual cron curl gpg jq logrotate rsyslog sudo tcpdump xfsprogs + apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs verifyRequiredPackagesInstalled "${REQUIRED_PACKAGES_FILE}" } From 70c697670e7f43965ad70c42f422ed4bc6d1427a Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 10:07:01 -0400 Subject: [PATCH 44/47] fix --- .pipelines/templates/.builder-release-template.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index 7069f8d5ed7..eacf1678016 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,7 +97,6 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -118,7 +117,6 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer - retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) From 556d47a7216dc46c93d23ef9842a681d2b538a24 Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 10:58:28 -0400 Subject: [PATCH 45/47] fix --- .../packer/trim_2604_cvm_packages_spec.sh | 26 +++++++++++++++++++ ...inimal-cvm-marked-for-removal-packages.txt | 1 - .../ubuntu-2604-cvm/trim-2604-cvm-packages.sh | 10 ++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh index 24b3350e413..d0593d10d9e 100644 --- a/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh +++ b/spec/vhdbuilder/packer/trim_2604_cvm_packages_spec.sh @@ -47,6 +47,32 @@ Describe 'trim-2604-cvm-packages' The output should include "apt-get -o DPkg::Lock::Timeout=300 purge -y --no-auto-remove --allow-remove-essential remove-me" The output should not include "allow-remove-essential absent-package" The output should include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs" + The output should not include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs tcpdump" + End + + It 'marks installed tcpdump runtime packages as manual' + printf '%s\n' remove-me > "${MARKED_FOR_REMOVAL_PACKAGES_FILE}" + printf '%s\n' required-package > "${REQUIRED_PACKAGES_FILE}" + + dpkg-query() { + for argument in "$@"; do + package="${argument}" + done + case "${package}" in + remove-me|required-package|libc6|libpcap0.8t64|libssl3t64|systemd|tcpdump) + echo installed + ;; + *) + return 1 + ;; + esac + } + apt-get() { return 0; } + apt-mark() { echo "apt-mark $*"; } + + When call main + The status should be success + The output should include "apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs libc6 libpcap0.8t64 libssl3t64 systemd tcpdump" End It 'fails when the removal list is empty' diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt index a9d5b9b5678..31376a74597 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt @@ -148,7 +148,6 @@ libnuma1 libopeniscsiusr libpackagekit-glib2-18 libpam-cap -libpcap0.8t64 libperl5.40 libpipeline1 libplymouth5 diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh index 232c9c0f69d..400037afc36 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh +++ b/vhdbuilder/packer/ubuntu-2604-cvm/trim-2604-cvm-packages.sh @@ -50,6 +50,8 @@ verifyForbiddenPackagesAbsent() { main() { local package + local -a manual_packages=(cron curl gpg jq logrotate rsyslog sudo xfsprogs) + local -a optional_manual_packages=(libc6 libpcap0.8t64 libssl3t64 systemd tcpdump) local -a purge_packages=() case "${1:-}" in @@ -81,7 +83,13 @@ main() { echo "No installed server-cvm packages marked for removal were found" fi - apt-mark manual cron curl gpg jq logrotate rsyslog sudo xfsprogs + for package in "${optional_manual_packages[@]}"; do + if [ "$(dpkg-query -W -f='${db:Status-Status}' "${package}" 2>/dev/null || true)" = "installed" ]; then + manual_packages+=("${package}") + fi + done + + apt-mark manual "${manual_packages[@]}" verifyRequiredPackagesInstalled "${REQUIRED_PACKAGES_FILE}" } From 3c54d70b3f80e0178152d445852242715253cb9d Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 11:10:09 -0400 Subject: [PATCH 46/47] fix --- .pipelines/templates/.builder-release-template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pipelines/templates/.builder-release-template.yaml b/.pipelines/templates/.builder-release-template.yaml index eacf1678016..7069f8d5ed7 100644 --- a/.pipelines/templates/.builder-release-template.yaml +++ b/.pipelines/templates/.builder-release-template.yaml @@ -97,6 +97,7 @@ steps: make -f packer.mk run-packer condition: ne(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) @@ -117,6 +118,7 @@ steps: make -f packer.mk run-imagecustomizer condition: eq(variables['BUILDER'], 'imagecustomizer') displayName: Build VHD - Image Customizer + retryCountOnTaskFailure: 3 env: OS_TYPE: Linux GIT_VERSION: $(Build.SourceVersion) From 2c041cc441c358c9e0c202703ef1b1a41697905b Mon Sep 17 00:00:00 2001 From: Zachary Bailey Date: Fri, 18 Sep 2026 11:57:53 -0400 Subject: [PATCH 47/47] fix --- .../2604-minimal-cvm-marked-for-removal-packages.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt index 31376a74597..30324ef5dad 100644 --- a/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt +++ b/vhdbuilder/packer/ubuntu-2604-cvm/2604-minimal-cvm-marked-for-removal-packages.txt @@ -107,7 +107,6 @@ libgpm2 libgprofng0 libgstreamer1.0-0 libgudev-1.0-0 -libibverbs1 libintl-perl libintl-xs-perl libip4tc2 @@ -139,7 +138,6 @@ libncurses6 libnetfilter-conntrack3 libnewt0.52 libnfnetlink0 -libnl-route-3-200 libnspr4 libnss-systemd libnss3