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
243 changes: 145 additions & 98 deletions docs/imagecustomizer/api/cosi.md

Large diffs are not rendered by default.

57 changes: 46 additions & 11 deletions toolkit/tools/pkg/imagecustomizerlib/cosicommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import (
"path"
"path/filepath"
"runtime"
"strings"

"github.com/microsoft/azurelinux/toolkit/tools/imagegen/diskutils"
"github.com/microsoft/azurelinux/toolkit/tools/internal/logger"
"github.com/microsoft/azurelinux/toolkit/tools/internal/safeloopback"
"github.com/microsoft/azurelinux/toolkit/tools/internal/shell"
)

type ImageBuildData struct {
Source string
KnownInfo outputPartitionMetadata
Metadata *Image
Metadata *FileSystem
VeritySource string
}

Expand Down Expand Up @@ -48,7 +50,7 @@ func convertToCosi(ic *ImageCustomizerParameters) error {
}

err = buildCosiFile(outputDir, ic.outputImageFile, partitionMetadataOutput, ic.verityMetadata,
ic.partUuidToFstabEntry, ic.imageUuidStr, ic.osRelease)
ic.partUuidToFstabEntry, ic.imageUuidStr, ic.osRelease, ic.osPackages)
if err != nil {
return fmt.Errorf("failed to build COSI file:\n%w", err)
}
Expand All @@ -65,7 +67,7 @@ func convertToCosi(ic *ImageCustomizerParameters) error {

func buildCosiFile(sourceDir string, outputFile string, partitions []outputPartitionMetadata,
verityMetadata []verityDeviceMetadata, partUuidToFstabEntry map[string]diskutils.FstabEntry,
imageUuidStr string, osRelease string,
imageUuidStr string, osRelease string, osPackages []OsPackage,
) error {
// Pre-compute a map for quick lookup of partition metadata by UUID
partUuidToMetadata := make(map[string]outputPartitionMetadata)
Expand Down Expand Up @@ -93,7 +95,7 @@ func buildCosiFile(sourceDir string, outputFile string, partitions []outputParti
continue
}

metadataImage := Image{
metadataImage := FileSystem{
Image: ImageFile{
Path: path.Join("images", partition.PartitionFilename),
UncompressedSize: partition.UncompressedSize,
Expand All @@ -118,7 +120,7 @@ func buildCosiFile(sourceDir string, outputFile string, partitions []outputParti
return fmt.Errorf("missing metadata for hash partition UUID:\n%s", verity.hashPartUuid)
}

metadataImage.Verity = &Verity{
metadataImage.Verity = &VerityConfig{
Roothash: verity.rootHash,
Image: ImageFile{
Path: path.Join("images", hashPartition.PartitionFilename),
Expand Down Expand Up @@ -146,11 +148,12 @@ func buildCosiFile(sourceDir string, outputFile string, partitions []outputParti
}

metadata := MetadataJson{
Version: "1.0",
OsArch: getArchitectureForCosi(),
Id: imageUuidStr,
Images: make([]Image, len(imageData)),
OsRelease: osRelease,
Version: "1.0",
OsArch: getArchitectureForCosi(),
Id: imageUuidStr,
Images: make([]FileSystem, len(imageData)),
OsRelease: osRelease,
OsPackages: osPackages,
}

// Copy updated metadata
Expand Down Expand Up @@ -281,7 +284,7 @@ func populateMetadata(data *ImageBuildData) error {
return nil
}

func populateVerityMetadata(source string, verity *Verity) error {
func populateVerityMetadata(source string, verity *VerityConfig) error {
if source == "" && verity == nil {
return nil
}
Expand All @@ -303,3 +306,35 @@ func getArchitectureForCosi() string {
}
return runtime.GOARCH
}

func getAllPackagesFromChroot(imageConnection *ImageConnection) ([]OsPackage, error) {
var out string
err := imageConnection.Chroot().UnsafeRun(func() error {
var err error
out, _, err = shell.Execute(
"rpm", "-qa", "--queryformat", "%{NAME} %{VERSION} %{RELEASE} %{ARCH}\n",
)
return err
})
if err != nil {
return nil, fmt.Errorf("failed to get RPM output from chroot:\n%w", err)
}

lines := strings.Split(strings.TrimSpace(out), "\n")
var packages []OsPackage
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) != 4 {
return nil, fmt.Errorf("malformed RPM line encountered while parsing installed RPMs for COSI: %q", line)
}
packages = append(packages, OsPackage{
Name: parts[0],
Version: parts[1],
Release: parts[2],
Arch: parts[3],
})
}

return packages, nil

}
34 changes: 21 additions & 13 deletions toolkit/tools/pkg/imagecustomizerlib/cosimetadata.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package imagecustomizerlib

type MetadataJson struct {
Version string `json:"version"`
OsArch string `json:"osArch"`
Images []Image `json:"images"`
OsRelease string `json:"osRelease"`
Id string `json:"id"`
Version string `json:"version"`
OsArch string `json:"osArch"`
Images []FileSystem `json:"images"`
OsRelease string `json:"osRelease"`
Id string `json:"id"`
OsPackages []OsPackage `json:"osPackages"`
}

type Image struct {
Image ImageFile `json:"image"`
MountPoint string `json:"mountPoint"`
FsType string `json:"fsType"`
FsUuid string `json:"fsUuid"`
PartType string `json:"partType"`
Verity *Verity `json:"verity"`
type FileSystem struct {
Image ImageFile `json:"image"`
MountPoint string `json:"mountPoint"`
FsType string `json:"fsType"`
FsUuid string `json:"fsUuid"`
PartType string `json:"partType"`
Verity *VerityConfig `json:"verity"`
}

type Verity struct {
type VerityConfig struct {
Image ImageFile `json:"image"`
Roothash string `json:"roothash"`
}
Expand All @@ -28,3 +29,10 @@ type ImageFile struct {
UncompressedSize uint64 `json:"uncompressedSize"`
Sha384 string `json:"sha384"`
}

type OsPackage struct {
Name string `json:"name"`
Version string `json:"version"`
Release string `json:"release"`
Arch string `json:"arch"`
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
func customizePartitionsUsingFileCopy(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config,
buildImageFile string, newBuildImageFile string,
) (map[string]string, error) {
existingImageConnection, _, _, err := connectToExistingImage(buildImageFile, buildDir, "imageroot", false)
existingImageConnection, _, _, _, err := connectToExistingImage(buildImageFile, buildDir, "imageroot", false)
if err != nil {
return nil, err
}
Expand Down
20 changes: 11 additions & 9 deletions toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type ImageCustomizerParameters struct {

partUuidToFstabEntry map[string]diskutils.FstabEntry
osRelease string
osPackages []OsPackage
}

type verityDeviceMetadata struct {
Expand Down Expand Up @@ -463,7 +464,7 @@ func customizeOSContents(ic *ImageCustomizerParameters) error {
}

// Customize the raw image file.
partUuidToFstabEntry, baseImageVerityMetadata, osRelease, err := customizeImageHelper(ic.buildDirAbs, ic.configPath,
partUuidToFstabEntry, baseImageVerityMetadata, osRelease, osPackages, err := customizeImageHelper(ic.buildDirAbs, ic.configPath,
ic.config, ic.rawImageFile, ic.rpmsSources, ic.useBaseImageRpmRepos, partitionsCustomized, ic.imageUuidStr, ic.packageSnapshotTime)
if err != nil {
return err
Expand All @@ -481,6 +482,7 @@ func customizeOSContents(ic *ImageCustomizerParameters) error {
ic.partUuidToFstabEntry = partUuidToFstabEntry
ic.baseImageVerityMetadata = baseImageVerityMetadata
ic.osRelease = osRelease
ic.osPackages = osPackages

// For COSI, always shrink the filesystems.
shrinkPartitions := ic.outputImageFormat == imagecustomizerapi.ImageFormatTypeCosi
Expand Down Expand Up @@ -853,20 +855,20 @@ func validateOutput(baseConfigPath string, output imagecustomizerapi.Output, out
func customizeImageHelper(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config,
rawImageFile string, rpmsSources []string, useBaseImageRpmRepos bool, partitionsCustomized bool,
imageUuidStr string, packageSnapshotTime string,
) (map[string]diskutils.FstabEntry, []verityDeviceMetadata, string, error) {
) (map[string]diskutils.FstabEntry, []verityDeviceMetadata, string, []OsPackage, error) {
logger.Log.Debugf("Customizing OS")

imageConnection, partUuidToFstabEntry, baseImageVerityMetadata, err := connectToExistingImage(rawImageFile,
imageConnection, partUuidToFstabEntry, baseImageVerityMetadata, osPackages, err := connectToExistingImage(rawImageFile,
buildDir, "imageroot", true)
if err != nil {
return nil, nil, "", err
return nil, nil, "", nil, err
}
defer imageConnection.Close()

// Extract OS release info from rootfs for COSI
osRelease, err := extractOSRelease(imageConnection)
if err != nil {
return nil, nil, "", fmt.Errorf("failed to extract OS release from rootfs partition:\n%w", err)
return nil, nil, "", nil, fmt.Errorf("failed to extract OS release from rootfs partition:\n%w", err)
}

imageConnection.Chroot().UnsafeRun(func() error {
Expand All @@ -878,7 +880,7 @@ func customizeImageHelper(buildDir string, baseConfigPath string, config *imagec

err = validateVerityMountPaths(imageConnection, config, partUuidToFstabEntry)
if err != nil {
return nil, nil, "", fmt.Errorf("verity validation failed:\n%w", err)
return nil, nil, "", nil, fmt.Errorf("verity validation failed:\n%w", err)
}

// Do the actual customizations.
Expand All @@ -890,15 +892,15 @@ func customizeImageHelper(buildDir string, baseConfigPath string, config *imagec
warnOnLowFreeSpace(buildDir, imageConnection)

if err != nil {
return nil, nil, "", err
return nil, nil, "", nil, err
}

err = imageConnection.CleanClose()
if err != nil {
return nil, nil, "", err
return nil, nil, "", nil, err
}

return partUuidToFstabEntry, baseImageVerityMetadata, osRelease, nil
return partUuidToFstabEntry, baseImageVerityMetadata, osRelease, osPackages, nil
}

func shrinkFilesystemsHelper(buildImageFile string) error {
Expand Down
12 changes: 9 additions & 3 deletions toolkit/tools/pkg/imagecustomizerlib/imageutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@ import (
type installOSFunc func(imageChroot *safechroot.Chroot) error

func connectToExistingImage(imageFilePath string, buildDir string, chrootDirName string, includeDefaultMounts bool,
) (*ImageConnection, map[string]diskutils.FstabEntry, []verityDeviceMetadata, error) {
) (*ImageConnection, map[string]diskutils.FstabEntry, []verityDeviceMetadata, []OsPackage, error) {
imageConnection := NewImageConnection()

partUuidToMountPath, verityMetadata, err := connectToExistingImageHelper(imageConnection, imageFilePath, buildDir,
chrootDirName, includeDefaultMounts)
if err != nil {
imageConnection.Close()
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
return imageConnection, partUuidToMountPath, verityMetadata, nil

packages, err := getAllPackagesFromChroot(imageConnection)
if err != nil {
return nil, nil, nil, nil, err
}

return imageConnection, partUuidToMountPath, verityMetadata, packages, nil
}

func connectToExistingImageHelper(imageConnection *ImageConnection, imageFilePath string,
Expand Down
2 changes: 1 addition & 1 deletion toolkit/tools/pkg/imagecustomizerlib/liveosisobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func createLiveOSFromRawHelper(buildDir, baseConfigPath string, inputArtifactsSt
}()

logger.Log.Debugf("Connecting to raw image (%s)", rawImageFile)
rawImageConnection, _, _, err := connectToExistingImage(rawImageFile, isoBuildDir, "readonly-rootfs-mount", false /*includeDefaultMounts*/)
rawImageConnection, _, _, _, err := connectToExistingImage(rawImageFile, isoBuildDir, "readonly-rootfs-mount", false /*includeDefaultMounts*/)
if err != nil {
return err
}
Expand Down
Loading