From 82ac185e7c93e560034c57e75e380da408ee40a4 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Mon, 17 Mar 2025 17:45:46 +0100 Subject: [PATCH] cloudapi: Disk customization conversion Convert from cloudapi Disk customizations to the blueprint types. --- internal/cloudapi/v2/compose.go | 102 +++++++++++++++ internal/cloudapi/v2/compose_test.go | 181 +++++++++++++++++++++++++++ 2 files changed, 283 insertions(+) diff --git a/internal/cloudapi/v2/compose.go b/internal/cloudapi/v2/compose.go index a0f16bddf..a84926731 100644 --- a/internal/cloudapi/v2/compose.go +++ b/internal/cloudapi/v2/compose.go @@ -495,6 +495,12 @@ func (rbp *Blueprint) GetCustomizationsFromBlueprintRequest() (*blueprint.Custom c.RHSM = bpRhsm } + disk, err := convertDiskCustomizations(rbpc.Disk) + if err != nil { + return nil, err + } + c.Disk = disk + return c, nil } @@ -1053,6 +1059,12 @@ func (request *ComposeRequest) GetBlueprintFromCustomizations() (blueprint.Bluep } bp.Customizations.RHSM = bpRhsm + + } + + bp.Customizations.Disk, err = convertDiskCustomizations(request.Customizations.Disk) + if err != nil { + return bp, err } if cacerts := request.Customizations.Cacerts; cacerts != nil { @@ -1276,3 +1288,93 @@ func (request *ComposeRequest) GetImageRequests(distroFactory *distrofactory.Fac } return irs, nil } + +func convertDiskCustomizations(disk *Disk) (*blueprint.DiskCustomization, error) { + if disk == nil { + return nil, nil + } + + bpDisk := &blueprint.DiskCustomization{ + MinSize: common.DerefOrDefault(disk.Minsize), + Type: string(common.DerefOrDefault(disk.Type)), + } + + for idx, partition := range disk.Partitions { + // partition successfully converts to all three types, so convert to + // filesystem to sniff the type string + sniffer, err := partition.AsFilesystemTyped() + if err != nil { + return nil, fmt.Errorf("failed to deserialize disk customization partition %d", idx) + } + + var bpPartition blueprint.PartitionCustomization + switch partType := common.DerefOrDefault(sniffer.Type); string(partType) { + case string(Plain): + fs, err := partition.AsFilesystemTyped() + if err != nil { + return nil, fmt.Errorf("failed to deserialize disk customization partition %d with type %q", idx, partType) + } + bpPartition = blueprint.PartitionCustomization{ + Type: string(common.DerefOrDefault(fs.Type)), + PartType: common.DerefOrDefault(fs.PartType), + MinSize: common.DerefOrDefault(fs.Minsize), + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: fs.Mountpoint, + Label: common.DerefOrDefault(fs.Label), + FSType: string(common.DerefOrDefault(fs.FsType)), + }, + } + case string(Btrfs): + btrfsVol, err := partition.AsBtrfsVolume() + if err != nil { + return nil, fmt.Errorf("failed to deserialize disk customization partition %d with type %q", idx, partType) + } + + bpPartition = blueprint.PartitionCustomization{ + Type: string(common.DerefOrDefault(btrfsVol.Type)), + PartType: common.DerefOrDefault(btrfsVol.PartType), + MinSize: common.DerefOrDefault(btrfsVol.Minsize), + } + + for _, subvol := range btrfsVol.Subvolumes { + bpSubvol := blueprint.BtrfsSubvolumeCustomization{ + Name: subvol.Name, + Mountpoint: subvol.Mountpoint, + } + bpPartition.Subvolumes = append(bpPartition.Subvolumes, bpSubvol) + } + case string(Lvm): + vg, err := partition.AsVolumeGroup() + if err != nil { + return nil, fmt.Errorf("failed to deserialize disk customization partition %d with type %q", idx, partType) + } + bpPartition = blueprint.PartitionCustomization{ + Type: string(common.DerefOrDefault(vg.Type)), + PartType: common.DerefOrDefault(vg.PartType), + MinSize: common.DerefOrDefault(vg.Minsize), + VGCustomization: blueprint.VGCustomization{ + Name: common.DerefOrDefault(vg.Name), + }, + } + + for _, lv := range vg.LogicalVolumes { + bpLV := blueprint.LVCustomization{ + Name: common.DerefOrDefault(lv.Name), + MinSize: common.DerefOrDefault(lv.Minsize), + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: lv.Mountpoint, + Label: common.DerefOrDefault(lv.Label), + FSType: string(common.DerefOrDefault(lv.FsType)), + }, + } + bpPartition.LogicalVolumes = append(bpPartition.LogicalVolumes, bpLV) + } + default: + return nil, fmt.Errorf("disk customization partition %d has invalid or unknown type %q", idx, partType) + + } + bpDisk.Partitions = append(bpDisk.Partitions, bpPartition) + } + + return bpDisk, nil +} diff --git a/internal/cloudapi/v2/compose_test.go b/internal/cloudapi/v2/compose_test.go index 5f87806a6..9f9d95da3 100644 --- a/internal/cloudapi/v2/compose_test.go +++ b/internal/cloudapi/v2/compose_test.go @@ -6,6 +6,7 @@ import ( repos "github.com/osbuild/images/data/repositories" "github.com/osbuild/images/pkg/customizations/subscription" + "github.com/osbuild/images/pkg/datasizes" "github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/distrofactory" "github.com/osbuild/images/pkg/reporegistry" @@ -177,6 +178,60 @@ func GetTestBlueprint() blueprint.Blueprint { }, }, }, + Disk: &blueprint.DiskCustomization{ + MinSize: 10, + Partitions: []blueprint.PartitionCustomization{ + { + Type: "plain", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + FSType: "xfs", + Label: "data", + Mountpoint: "/data", + }, + }, + { + Type: "btrfs", + MinSize: 100 * datasizes.MiB, + BtrfsVolumeCustomization: blueprint.BtrfsVolumeCustomization{ + Subvolumes: []blueprint.BtrfsSubvolumeCustomization{ + { + Name: "+subvols/db1", + Mountpoint: "/data/db1", + }, + { + Name: "+subvols/db2", + Mountpoint: "/data/db2", + }, + }, + }, + }, + { + Type: "lvm", + MinSize: 10 * datasizes.GiB, + PartType: "E6D6D379-F507-44C2-A23C-238F2A3DF928", + VGCustomization: blueprint.VGCustomization{ + Name: "vg000001", + LogicalVolumes: []blueprint.LVCustomization{ + { + Name: "rootlv", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/", + FSType: "ext4", + }, + }, + { + Name: "homelv", + MinSize: 3 * datasizes.GiB, + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/home", + Label: "home", + }, + }, + }, + }, + }, + }, + }, } return expected @@ -210,6 +265,61 @@ func TestGetBlueprintFromCustomizations(t *testing.T) { var fileGroup File_Group require.NoError(t, fileGroup.FromFileGroup0("root")) + var plainPart Partition + require.NoError(t, plainPart.FromFilesystemTyped( + FilesystemTyped{ + Type: common.ToPtr(Plain), + Minsize: nil, + FsType: common.ToPtr(FilesystemTypedFsTypeXfs), + Label: common.ToPtr("data"), + Mountpoint: "/data", + }, + )) + + var btrfsPart Partition + require.NoError(t, btrfsPart.FromBtrfsVolume( + BtrfsVolume{ + Type: common.ToPtr(Btrfs), + Minsize: common.ToPtr(uint64(100 * datasizes.MiB)), + Subvolumes: []BtrfsSubvolume{ + { + Mountpoint: "/data/db1", + Name: "+subvols/db1", + }, + { + Mountpoint: "/data/db2", + Name: "+subvols/db2", + }, + }, + }, + )) + + var vgPart Partition + require.NoError(t, vgPart.FromVolumeGroup( + VolumeGroup{ + Type: common.ToPtr(Lvm), + Minsize: common.ToPtr(uint64(10 * datasizes.GiB)), + Name: common.ToPtr("vg000001"), + PartType: common.ToPtr("E6D6D379-F507-44C2-A23C-238F2A3DF928"), + LogicalVolumes: []LogicalVolume{ + { + FsType: common.ToPtr(LogicalVolumeFsTypeExt4), + Label: nil, + Minsize: nil, + Mountpoint: "/", + Name: common.ToPtr("rootlv"), + }, + { + FsType: nil, + Label: common.ToPtr("home"), + Minsize: common.ToPtr(uint64(3) * datasizes.GiB), + Mountpoint: "/home", + Name: common.ToPtr("homelv"), + }, + }, + }, + )) + // Construct the compose request with customizations cr = ComposeRequest{Customizations: &Customizations{ Users: &[]User{ @@ -340,6 +450,14 @@ func TestGetBlueprintFromCustomizations(t *testing.T) { }, }, }, + Disk: &Disk{ + Minsize: common.ToPtr(uint64(10)), + Partitions: []Partition{ + plainPart, + btrfsPart, + vgPart, + }, + }, }} bp, err = cr.GetBlueprintFromCustomizations() @@ -394,6 +512,61 @@ func TestGetBlueprintFromCompose(t *testing.T) { var fileGroup BlueprintFile_Group require.NoError(t, fileGroup.FromBlueprintFileGroup0("root")) + var plainPart Partition + require.NoError(t, plainPart.FromFilesystemTyped( + FilesystemTyped{ + Type: common.ToPtr(Plain), + Minsize: nil, + FsType: common.ToPtr(FilesystemTypedFsTypeXfs), + Label: common.ToPtr("data"), + Mountpoint: "/data", + }, + )) + + var btrfsPart Partition + require.NoError(t, btrfsPart.FromBtrfsVolume( + BtrfsVolume{ + Type: common.ToPtr(Btrfs), + Minsize: common.ToPtr(uint64(100 * datasizes.MiB)), + Subvolumes: []BtrfsSubvolume{ + { + Mountpoint: "/data/db1", + Name: "+subvols/db1", + }, + { + Mountpoint: "/data/db2", + Name: "+subvols/db2", + }, + }, + }, + )) + + var vgPart Partition + require.NoError(t, vgPart.FromVolumeGroup( + VolumeGroup{ + Type: common.ToPtr(Lvm), + Minsize: common.ToPtr(uint64(10 * datasizes.GiB)), + Name: common.ToPtr("vg000001"), + PartType: common.ToPtr("E6D6D379-F507-44C2-A23C-238F2A3DF928"), + LogicalVolumes: []LogicalVolume{ + { + FsType: common.ToPtr(LogicalVolumeFsTypeExt4), + Label: nil, + Minsize: nil, + Mountpoint: "/", + Name: common.ToPtr("rootlv"), + }, + { + FsType: nil, + Label: common.ToPtr("home"), + Minsize: common.ToPtr(uint64(3) * datasizes.GiB), + Mountpoint: "/home", + Name: common.ToPtr("homelv"), + }, + }, + }, + )) + // Construct the compose request with a blueprint cr = ComposeRequest{Blueprint: &Blueprint{ Name: "empty blueprint", @@ -526,6 +699,14 @@ func TestGetBlueprintFromCompose(t *testing.T) { }, }, }, + Disk: &Disk{ + Minsize: common.ToPtr(uint64(10)), + Partitions: []Partition{ + plainPart, + btrfsPart, + vgPart, + }, + }, }, }}