Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion toolkit/tools/pkg/imagecustomizerlib/configvalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ var (
ErrAdditionalDirsSourceIsFile = NewImageCustomizerError("Validation:AdditionalDirsSourceIsFile", "additionalDirs source exists but is a file")
ErrAdditionalDirsSourceNotFound = NewImageCustomizerError("Validation:AdditionalDirsSourceNotFound", "additionalDirs source does not exist")
ErrInvalidPackageSnapshotTime = NewImageCustomizerError("Validation:InvalidPackageSnapshotTime", "invalid command-line option '--package-snapshot-time'")
ErrUnsupportedFedoraFeature = NewImageCustomizerError("Validation:UnsupportedFedoraFeature", "unsupported feature for Fedora images")
ErrInvalidOutputSelinuxPolicyPathArg = NewImageCustomizerError("Validation:InvalidOutputSelinuxPolicyPathArg", "invalid command-line option '--output-selinux-policy-path'")
ErrOutputSelinuxPolicyPathIsFileArg = NewImageCustomizerError("Validation:OutputSelinuxPolicyPathIsFileArg", "path exists but is a file")
ErrInvalidOutputSelinuxPolicyPathConfig = NewImageCustomizerError("Validation:InvalidOutputSelinuxPolicyPathConfig", "invalid config file property 'output.selinuxPolicyPath'")
Expand Down
4 changes: 2 additions & 2 deletions toolkit/tools/pkg/imagecustomizerlib/convertimage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func testConvertImageRawToCosi(t *testing.T, baseImageInfo testBaseImageInfo) {
if baseImageInfo.Distro == baseImageDistroUbuntu {
// This check should be removed once bootloader hard-reset support is added for Ubuntu.
// It will fail once this support is added.
assert.ErrorContains(t, err, "bootloader hard-reset is not supported for Ubuntu images")
assert.ErrorIs(t, err, ErrUbuntuUnsupportedBootloaderHardReset)
return
}
if !assert.NoError(t, err) {
Expand Down Expand Up @@ -197,7 +197,7 @@ func testConvertImageRawToCosiWithCompression(t *testing.T, baseImageInfo testBa
if baseImageInfo.Distro == baseImageDistroUbuntu {
// This check should be removed once bootloader hard-reset support is added for Ubuntu.
// It will fail once this support is added.
assert.ErrorContains(t, err, "bootloader hard-reset is not supported for Ubuntu images")
assert.ErrorIs(t, err, ErrUbuntuUnsupportedBootloaderHardReset)
return
}
if !assert.NoError(t, err) {
Expand Down
4 changes: 4 additions & 0 deletions toolkit/tools/pkg/imagecustomizerlib/distrohandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const (
var (
ErrUnsupportedDistroVersion = NewImageCustomizerError("Validation:UnsupportedDistroVersion", "base image has unsupported distro version")
ErrUnsupportedDistroVersionSuffix = fmt.Sprintf("preview feature '%s' may be specified to use unsupported versions", imagecustomizerapi.PreviewFeatureUnsupportedDistroVersion)

ErrUnsupportedDistroApi = NewImageCustomizerError("Validation:UnsupportedDistroApi", "unsupported API for distro")
ErrUnsupportedPackageSnapshotTime = NewImageCustomizerError("Validation:UnsupportedPackageSnapshotTime", "package snapshot time API is not supported")
ErrUnsupportedRpmSources = NewImageCustomizerError("Validation:UnsupportedRpmSources", "RPM sources API is not supported")
)

// DistroHandler represents the interface for distribution-specific configuration
Expand Down
10 changes: 10 additions & 0 deletions toolkit/tools/pkg/imagecustomizerlib/distrohandler_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ func (d *aclDistroHandler) ValidateConfig(rc *ResolvedConfig) error {
}
}

err := d.checkForUnsupportedApis(rc)
if err != nil {
return fmt.Errorf("%w (distro='%s', versionid='%s'):\n%w", ErrUnsupportedDistroApi, d.targetOs.Distro,
d.targetOs.VersionId, err)
}

return nil
}

func (d *aclDistroHandler) checkForUnsupportedApis(rc *ResolvedConfig) error {
if rc.Storage.CustomizePartitions() {
return fmt.Errorf("storage repartitioning is not yet supported for ACL")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,24 @@ func (d *azureLinux4DistroHandler) ValidateConfig(rc *ResolvedConfig) error {
}
}

err := d.checkForUnsupportedApis(rc)
if err != nil {
return fmt.Errorf("%w (distro='%s', versionid='%s'):\n%w", ErrUnsupportedDistroApi, d.targetOs.Distro,
d.targetOs.VersionId, err)
}

return nil
}

func (d *azureLinux4DistroHandler) checkForUnsupportedApis(rc *ResolvedConfig) error {
if rc.HasPackageSnapshotTime() {
return ErrUnsupportedPackageSnapshotTime
}

switch rc.OutputImageFormat {
case imagecustomizerapi.ImageFormatTypeIso, imagecustomizerapi.ImageFormatTypePxeDir,
imagecustomizerapi.ImageFormatTypePxeTar:
return fmt.Errorf("ISO and PXE output formats are not supported for Azure Linux 4.0")
return fmt.Errorf("ISO and PXE output formats are not supported yet for Azure Linux 4.0 images")
}

return nil
Expand Down
12 changes: 11 additions & 1 deletion toolkit/tools/pkg/imagecustomizerlib/distrohandler_fedora.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,18 @@ func (d *fedoraDistroHandler) ValidateConfig(rc *ResolvedConfig) error {
}
}

err := d.checkForUnsupportedApis(rc)
if err != nil {
return fmt.Errorf("%w (distro='%s', versionid='%s'):\n%w", ErrUnsupportedDistroApi, d.targetOs.Distro,
d.targetOs.VersionId, err)
}

return nil
}

func (d *fedoraDistroHandler) checkForUnsupportedApis(rc *ResolvedConfig) error {
if rc.HasPackageSnapshotTime() {
return fmt.Errorf("Package snapshotting API not supported for Fedora:\n%w", ErrUnsupportedFedoraFeature)
return ErrUnsupportedPackageSnapshotTime
}

return nil
Expand Down
92 changes: 92 additions & 0 deletions toolkit/tools/pkg/imagecustomizerlib/distrohandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package imagecustomizerlib
import (
"os"
"path/filepath"
"slices"
"testing"

"github.com/microsoft/azure-linux-image-tools/toolkit/tools/imagecustomizerapi"
Expand Down Expand Up @@ -143,3 +144,94 @@ func TestAclValidateConfigPackageOpsRequireToolsDir(t *testing.T) {
err = handler.ValidateConfig(rc)
assert.NoError(t, err)
}

func TestCustomizeImageUnsupportedPackageSnapshotTime(t *testing.T) {
for _, baseImageInfo := range slices.Concat([]testBaseImageInfo{testBaseImageAzl4CoreEfi}, baseImageUbuntuAll) {
t.Run(baseImageInfo.Name, func(t *testing.T) {
testCustomizeImageUnsupportedPackageSnapshotTimeHelper(t, baseImageInfo)
})
}
}

func testCustomizeImageUnsupportedPackageSnapshotTimeHelper(t *testing.T, baseImageInfo testBaseImageInfo) {
baseImage := checkSkipForCustomizeImage(t, baseImageInfo)

testTmpDir := filepath.Join(tmpDir, "TestCustomizeImageUnsupportedPackageSnapshotTime_"+baseImageInfo.Name)
defer os.RemoveAll(testTmpDir)

buildDir := filepath.Join(testTmpDir, "build")

options := ImageCustomizerOptions{
BuildDir: buildDir,
InputImageFile: baseImage,
OutputImageFile: "./out/image.vhdx",
OutputImageFormat: "vhdx",
UseBaseImageRpmRepos: true,
PreviewFeatures: baseImageInfo.PreviewFeatures,
}

config := &imagecustomizerapi.Config{
PreviewFeatures: []imagecustomizerapi.PreviewFeature{imagecustomizerapi.PreviewFeaturePackageSnapshotTime},
OS: &imagecustomizerapi.OS{},
}

options.PackageSnapshotTime = "2025-01-01"

err := CustomizeImage(t.Context(), testTmpDir, config, options)
assert.ErrorIs(t, err, ErrUnsupportedPackageSnapshotTime)
assert.ErrorIs(t, err, ErrUnsupportedDistroApi)

options.PackageSnapshotTime = ""
config.OS.Packages.SnapshotTime = "2025-01-01"

err = CustomizeImage(t.Context(), testTmpDir, config, options)
assert.ErrorIs(t, err, ErrUnsupportedPackageSnapshotTime)
assert.ErrorIs(t, err, ErrUnsupportedDistroApi)
}

func TestCustomizeImageUnsupportedRpmSources(t *testing.T) {
for _, baseImageInfo := range baseImageUbuntuAll {
t.Run(baseImageInfo.Name, func(t *testing.T) {
testCustomizeImageUnsupportedRpmSourcesHelper(t, baseImageInfo)
})
}
}

func testCustomizeImageUnsupportedRpmSourcesHelper(t *testing.T, baseImageInfo testBaseImageInfo) {
baseImage := checkSkipForCustomizeImage(t, baseImageInfo)

testTmpDir := filepath.Join(tmpDir, "TestCustomizeImageUnsupportedRpmSources_"+baseImageInfo.Name)
defer os.RemoveAll(testTmpDir)

buildDir := filepath.Join(testTmpDir, "build")

err := os.MkdirAll(testTmpDir, os.ModePerm)
if !assert.NoError(t, err) {
return
}

repoFile := filepath.Join(testTmpDir, "a.repo")
err = os.WriteFile(repoFile, []byte{}, os.ModePerm)
if !assert.NoError(t, err) {
return
}

options := ImageCustomizerOptions{
BuildDir: buildDir,
InputImageFile: baseImage,
OutputImageFile: "./out/image.vhdx",
OutputImageFormat: "vhdx",
UseBaseImageRpmRepos: true,
PreviewFeatures: baseImageInfo.PreviewFeatures,
}

config := &imagecustomizerapi.Config{
OS: &imagecustomizerapi.OS{},
}

options.RpmsSources = []string{repoFile}

err = CustomizeImage(t.Context(), testTmpDir, config, options)
assert.ErrorIs(t, err, ErrUnsupportedRpmSources)
assert.ErrorIs(t, err, ErrUnsupportedDistroApi)
}
52 changes: 32 additions & 20 deletions toolkit/tools/pkg/imagecustomizerlib/distrohandler_ubuntu.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ const (
grubEfiPackageDebianArm64 = "grub-efi-arm64"
)

var (
ErrUbuntuUnsupportedBootloaderHardReset = NewImageCustomizerError("Validation:UbuntuUnsupportedBootloaderHardReset", "bootloader hard-reset API is not supported yet for Ubuntu images")
Comment thread
cwize1 marked this conversation as resolved.
ErrUbuntuUnsupportedDisableBaseImageRepos = NewImageCustomizerError("Validation:UbuntuUnsupportedDisableBaseImageRepos", "disabling base image package repositories is not supported yet for Ubuntu images")
)

func newUbuntuDistroHandler(targetOs targetos.TargetOs) *ubuntuDistroHandler {
logger.Log.Debugf("Distro handler: Ubuntu (distro='%s', versionid='%s')", targetOs.Distro, targetOs.VersionId)

Expand Down Expand Up @@ -59,37 +64,45 @@ func (d *ubuntuDistroHandler) ValidateConfig(rc *ResolvedConfig) error {
}
}

// Check if Ubuntu is being used with bootloader hard-reset.
// Ubuntu bootloader config logic is not yet fully implemented.
if rc.BootLoader.ResetType == imagecustomizerapi.ResetBootLoaderTypeHard {
return ErrUbuntuBootLoaderHardReset
err := d.checkForUnsupportedApis(rc)
if err != nil {
return fmt.Errorf("%w (distro='%s', versionid='%s'):\n%w", ErrUnsupportedDistroApi, d.targetOs.Distro,
d.targetOs.VersionId, err)
}

return nil
}

// ManagePackages handles the complete package management workflow for Ubuntu
func (d *ubuntuDistroHandler) ManagePackages(ctx context.Context, buildDir string, baseConfigPath string,
config *imagecustomizerapi.OS, imageChroot *safechroot.Chroot, toolsChroot *safechroot.Chroot,
rpmsSources []string, useBaseImageRpmRepos bool, snapshotTime imagecustomizerapi.PackageSnapshotTime,
) error {
if len(rpmsSources) > 0 {
return fmt.Errorf("RPM sources are not supported for Ubuntu images:\n%w", ErrUnsupportedUbuntuFeature)
func (d *ubuntuDistroHandler) checkForUnsupportedApis(rc *ResolvedConfig) error {
// Check if Ubuntu is being used with bootloader hard-reset.
// Ubuntu bootloader config logic is not yet fully implemented.
if rc.BootLoader.ResetType == imagecustomizerapi.ResetBootLoaderTypeHard {
return ErrUbuntuUnsupportedBootloaderHardReset
}

if len(rc.Options.RpmsSources) > 0 {
return ErrUnsupportedRpmSources
}

// UseBaseImageRpmRepos defaults to true and is only false when the user explicitly
// passes --disable-base-image-rpm-repos. Ubuntu does not use RPM repos, so disabling
// them is not meaningful and likely indicates a configuration mistake.
if !useBaseImageRpmRepos {
return fmt.Errorf("Disabling base image RPM repositories is not supported for Ubuntu images:\n%w",
ErrUnsupportedUbuntuFeature)
if !rc.Options.UseBaseImageRpmRepos {
Comment thread
cwize1 marked this conversation as resolved.
return ErrUbuntuUnsupportedDisableBaseImageRepos
}

if config.Packages.SnapshotTime != "" {
return fmt.Errorf("package snapshotTime is not yet supported for Ubuntu images:\n%w",
ErrUnsupportedUbuntuFeature)
if rc.HasPackageSnapshotTime() {
return ErrUnsupportedPackageSnapshotTime
}

return nil
}

// ManagePackages handles the complete package management workflow for Ubuntu
func (d *ubuntuDistroHandler) ManagePackages(ctx context.Context, buildDir string, baseConfigPath string,
config *imagecustomizerapi.OS, imageChroot *safechroot.Chroot, toolsChroot *safechroot.Chroot,
rpmsSources []string, useBaseImageRpmRepos bool, snapshotTime imagecustomizerapi.PackageSnapshotTime,
) error {
return managePackagesDeb(ctx, config, imageChroot)
}

Expand All @@ -100,8 +113,7 @@ func (d *ubuntuDistroHandler) IsPackageInstalled(imageChroot safechroot.ChrootIn

func (d *ubuntuDistroHandler) GetPackageInformation(imageChroot *safechroot.Chroot, packageName string,
) (*PackageVersionInformation, error) {
return nil, fmt.Errorf("Getting package information is not supported yet for Ubuntu images:\n%w",
ErrUnsupportedUbuntuFeature)
return nil, fmt.Errorf("getting package information is not supported yet for Ubuntu images")
}

func (d *ubuntuDistroHandler) GetAllPackagesFromChroot(imageChroot safechroot.ChrootInterface) ([]OsPackage, error) {
Expand Down Expand Up @@ -194,7 +206,7 @@ func (d *ubuntuDistroHandler) ConfigureDiskBootLoader(imageConnection *imageconn
selinuxConfig imagecustomizerapi.SELinux, kernelCommandLine imagecustomizerapi.KernelCommandLine,
currentSELinuxMode imagecustomizerapi.SELinuxMode, newImage bool,
) error {
return ErrUbuntuBootLoaderHardReset
return ErrUbuntuUnsupportedBootloaderHardReset
}

func (d *ubuntuDistroHandler) ReadGrubConfigLinuxArgs(bootDir string) (map[string][]grubConfigLinuxArg, error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package imagecustomizerlib

import (
"os"
"path/filepath"
"testing"

"github.com/microsoft/azure-linux-image-tools/toolkit/tools/imagecustomizerapi"
"github.com/stretchr/testify/assert"
)

func TestCustomizeImageUbuntuUnsupportedAPIs(t *testing.T) {
for _, baseImageInfo := range baseImageUbuntuAll {
t.Run(baseImageInfo.Name, func(t *testing.T) {
testCustomizeImageUbuntuUnsupportedAPIsHelper(t, baseImageInfo)
})
}
}

func testCustomizeImageUbuntuUnsupportedAPIsHelper(t *testing.T, baseImageInfo testBaseImageInfo) {
baseImage := checkSkipForCustomizeImage(t, baseImageInfo)

testTmpDir := filepath.Join(tmpDir, "TestCustomizeImageUbuntuUnsupportedAPIs_"+baseImageInfo.Name)
defer os.RemoveAll(testTmpDir)

buildDir := filepath.Join(testTmpDir, "build")

options := ImageCustomizerOptions{
BuildDir: buildDir,
InputImageFile: baseImage,
OutputImageFile: "./out/image.vhdx",
OutputImageFormat: "vhdx",
UseBaseImageRpmRepos: true,
PreviewFeatures: baseImageInfo.PreviewFeatures,
}

config := &imagecustomizerapi.Config{
OS: &imagecustomizerapi.OS{},
}

config.OS.BootLoader.ResetType = imagecustomizerapi.ResetBootLoaderTypeHard

err := CustomizeImage(t.Context(), testTmpDir, config, options)
assert.ErrorIs(t, err, ErrUbuntuUnsupportedBootloaderHardReset)
assert.ErrorIs(t, err, ErrUnsupportedDistroApi)

config.OS.BootLoader.ResetType = imagecustomizerapi.ResetBootLoaderTypeDefault
options.UseBaseImageRpmRepos = false

err = CustomizeImage(t.Context(), testTmpDir, config, options)
assert.ErrorIs(t, err, ErrUbuntuUnsupportedDisableBaseImageRepos)
assert.ErrorIs(t, err, ErrUnsupportedDistroApi)
}
2 changes: 0 additions & 2 deletions toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ var (
ErrFedoraPreviewFeatureRequired = NewImageCustomizerError("Validation:FedoraPreviewFeatureRequired", fmt.Sprintf("preview feature '%s' required to customize Fedora base image", imagecustomizerapi.PreviewFeatureFedora))
ErrUbuntuPreviewFeatureRequired = NewImageCustomizerError("Validation:UbuntuPreviewFeatureRequired", fmt.Sprintf("preview feature '%s' required to customize Ubuntu base image", imagecustomizerapi.PreviewFeatureUbuntu))
ErrAzureContainerLinuxPreviewFeatureRequired = NewImageCustomizerError("Validation:AzureContainerLinuxPreviewFeatureRequired", fmt.Sprintf("preview feature '%s' required to customize Azure Container Linux base image", imagecustomizerapi.PreviewFeatureAzureContainerLinux))
ErrUbuntuBootLoaderHardReset = NewImageCustomizerError("Validation:UbuntuBootLoaderHardReset", "bootloader hard-reset is not supported for Ubuntu images")
ErrUnsupportedUbuntuFeature = NewImageCustomizerError("Validation:UnsupportedUbuntuFeature", "unsupported feature for Ubuntu images")
ErrInputImageOciPreviewRequired = NewImageCustomizerError("Validation:InputImageOciPreviewRequired", fmt.Sprintf("preview feature '%s' required to specify OCI input image", imagecustomizerapi.PreviewFeatureInputImageOci))
ErrConvertUnsupportedInputFormat = NewImageCustomizerError("Validation:ConvertUnsupportedInputFormat", "input image format is not supported")
ErrConvertBuildDirRequired = NewImageCustomizerError("Validation:ConvertBuildDirRequired", "build directory is required for cosi and baremetal-image output formats")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
// Azure Linux distro handler when ISO or PXE output is requested with
// an Azure Linux 4.0 base image. ISO and PXE outputs are not supported
// on Azure Linux 4.0; LiveOS tests expect this error in that case.
const azl4IsoPxeUnsupportedError = "ISO and PXE output formats are not supported for Azure Linux 4.0"
const azl4IsoPxeUnsupportedError = "ISO and PXE output formats are not supported yet for Azure Linux 4.0 images"

func createConfig(t *testing.T, baseImageVersion string, fileNames, kernelParameter string, initramfsType imagecustomizerapi.InitramfsImageType,
bootstrapFileUrl string, enlargeDisk, enableOsConfig, bootstrapPrereqs, twoKernels bool, kdumpBootFiles imagecustomizerapi.KdumpBootFilesType,
Expand Down
Loading