From e57cccc3fe9c66c0dccd882caa22285faca8bfca Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Fri, 25 Feb 2022 14:28:27 +0100 Subject: [PATCH] disk: NewPartitionTable can wrap plain partitions in LVM Add a new parameter `lvmify` to `NewPartitionTable` that, if set to `true`, will cause the root partition to be wrapped in LVM in case it is not in a LVM volume group. Set this to `false` for now so no actual change should happen anywhere. Layouts where the root is directly on a LUKS container are not yet supported. Add tests for this. --- internal/disk/disk_test.go | 34 ++++++++++++++++++-- internal/disk/partition_table.go | 47 +++++++++++++++++++++++++++- internal/distro/rhel85/distro.go | 2 +- internal/distro/rhel86/distro.go | 2 +- internal/distro/rhel90/distro.go | 2 +- internal/distro/rhel90beta/distro.go | 2 +- internal/osbuild2/device_test.go | 4 +-- internal/osbuild2/disk_test.go | 2 +- 8 files changed, 85 insertions(+), 10 deletions(-) diff --git a/internal/disk/disk_test.go b/internal/disk/disk_test.go index 8cc4a91f3..bd3a9b548 100644 --- a/internal/disk/disk_test.go +++ b/internal/disk/disk_test.go @@ -66,7 +66,7 @@ func TestDisk_DynamicallyResizePartitionTable(t *testing.T) { // math/rand is good enough in this case /* #nosec G404 */ rng := rand.New(rand.NewSource(0)) - newpt, err := NewPartitionTable(&pt, mountpoints, 1024, rng) + newpt, err := NewPartitionTable(&pt, mountpoints, 1024, false, rng) assert.NoError(t, err) assert.GreaterOrEqual(t, newpt.Size, expectedSize) } @@ -358,7 +358,7 @@ func TestCreatePartitionTable(t *testing.T) { rng := rand.New(rand.NewSource(13)) for name := range testPartitionTables { pt := testPartitionTables[name] - mpt, err := NewPartitionTable(&pt, bp, uint64(13*1024*1024), rng) + mpt, err := NewPartitionTable(&pt, bp, uint64(13*1024*1024), false, rng) assert.NoError(err, "Partition table generation failed: %s (%s)", name, err) assert.NotNil(mpt, "Partition table generation failed: %s (nil partition table)", name) assert.Greater(mpt.GetSize(), uint64(37*1024*1024*1024)) @@ -369,3 +369,33 @@ func TestCreatePartitionTable(t *testing.T) { assert.NotNil(mnt, "Partition table '%s': failed to find root mountable", name) } } + +func TestCreatePartitionTableLVMify(t *testing.T) { + assert := assert.New(t) + // math/rand is good enough in this case + /* #nosec G404 */ + rng := rand.New(rand.NewSource(13)) + for name := range testPartitionTables { + pt := testPartitionTables[name] + + if name == "btrfs" || name == "luks" { + assert.Panics(func() { + _, _ = NewPartitionTable(&pt, bp, uint64(13*1024*1024), true, rng) + }) + continue + } + + mpt, err := NewPartitionTable(&pt, bp, uint64(13*1024*1024), true, rng) + assert.NoError(err, "Partition table generation failed: %s (%s)", name, err) + + rootPath := entityPath(mpt, "/") + if rootPath == nil { + panic("no root mountpoint for PartitionTable") + } + + parent := rootPath[1] + _, ok := parent.(*LVMLogicalVolume) + assert.True(ok, "Partition table '%s': root's parent (%q) is not an LVM logical volume", name, parent) + } + +} diff --git a/internal/disk/partition_table.go b/internal/disk/partition_table.go index 59450b920..bac29aef5 100644 --- a/internal/disk/partition_table.go +++ b/internal/disk/partition_table.go @@ -18,13 +18,16 @@ type PartitionTable struct { ExtraPadding uint64 // Extra space at the end of the partition table (sectors) } -func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, rng *rand.Rand) (*PartitionTable, error) { +func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, lvmify bool, rng *rand.Rand) (*PartitionTable, error) { newPT := basePT.Clone().(*PartitionTable) for _, mnt := range mountpoints { size := newPT.AlignUp(clampFSSize(mnt.Mountpoint, mnt.MinSize)) if path := entityPath(newPT, mnt.Mountpoint); len(path) != 0 { resizeEntityBranch(path, size) } else { + if lvmify { + newPT.ensureLVM() + } if err := newPT.createFilesystem(mnt.Mountpoint, size); err != nil { return nil, err } @@ -484,3 +487,45 @@ func (pt *PartitionTable) GenUUID(rng *rand.Rand) { pt.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String() } } + +// ensureLVM will ensure that the root partition is on an LVM volume, i.e. if +// it currently is not, it will wrap it in one +func (pt *PartitionTable) ensureLVM() { + + rootPath := entityPath(pt, "/") + if rootPath == nil { + panic("no root mountpoint for PartitionTable") + } + + parent := rootPath[1] // NB: entityPath has reversed order + + if _, ok := parent.(*LVMLogicalVolume); ok { + return + } else if part, ok := parent.(*Partition); ok { + filesystem := part.Payload + + part.Payload = &LVMVolumeGroup{ + Name: "rootvg", + Description: "created via lvm2 and osbuild", + LogicalVolumes: []LVMLogicalVolume{ + { + Size: part.Size, + Name: "rootlv", + Payload: filesystem, + }, + }, + } + + // reset it so it will be grown later + part.Size = 0 + + if pt.Type == "gpt" { + part.Type = LVMPartitionGUID + } else { + part.Type = "8e" + } + + } else { + panic("unsupported parent for LVM") + } +} diff --git a/internal/distro/rhel85/distro.go b/internal/distro/rhel85/distro.go index 9c65fc77b..8d2d1f5de 100644 --- a/internal/distro/rhel85/distro.go +++ b/internal/distro/rhel85/distro.go @@ -324,7 +324,7 @@ func (t *imageType) getPartitionTable( imageSize := t.Size(options.Size) - return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, rng) + return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, false, rng) } func (t *imageType) PartitionType() string { diff --git a/internal/distro/rhel86/distro.go b/internal/distro/rhel86/distro.go index 027200a4d..82c9e30ee 100644 --- a/internal/distro/rhel86/distro.go +++ b/internal/distro/rhel86/distro.go @@ -394,7 +394,7 @@ func (t *imageType) getPartitionTable( imageSize := t.Size(options.Size) - return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, rng) + return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, false, rng) } func (t *imageType) getDefaultImageConfig() *distro.ImageConfig { diff --git a/internal/distro/rhel90/distro.go b/internal/distro/rhel90/distro.go index 1025e86f0..6fc4cbde9 100644 --- a/internal/distro/rhel90/distro.go +++ b/internal/distro/rhel90/distro.go @@ -382,7 +382,7 @@ func (t *imageType) getPartitionTable( imageSize := t.Size(options.Size) - return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, rng) + return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, false, rng) } func (t *imageType) getDefaultImageConfig() *distro.ImageConfig { diff --git a/internal/distro/rhel90beta/distro.go b/internal/distro/rhel90beta/distro.go index 1c84aef42..6976cbfd9 100644 --- a/internal/distro/rhel90beta/distro.go +++ b/internal/distro/rhel90beta/distro.go @@ -359,7 +359,7 @@ func (t *imageType) getPartitionTable( imageSize := t.Size(options.Size) - return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, rng) + return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, false, rng) } func (t *imageType) PartitionType() string { diff --git a/internal/osbuild2/device_test.go b/internal/osbuild2/device_test.go index 8b3492cb2..705a53122 100644 --- a/internal/osbuild2/device_test.go +++ b/internal/osbuild2/device_test.go @@ -18,7 +18,7 @@ func TestGenDeviceCreationStages(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng) assert.NoError(err) stages := GenDeviceCreationStages(pt, "image.raw") @@ -81,7 +81,7 @@ func TestGenDeviceFinishStages(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng) assert.NoError(err) stages := GenDeviceFinishStages(pt, "image.raw") diff --git a/internal/osbuild2/disk_test.go b/internal/osbuild2/disk_test.go index d912c49ec..524210acc 100644 --- a/internal/osbuild2/disk_test.go +++ b/internal/osbuild2/disk_test.go @@ -18,7 +18,7 @@ func TestGenImageKernelOptions(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng) assert.NoError(err) var uuid string