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.
This commit is contained in:
Christian Kellner 2022-02-25 14:28:27 +01:00
parent bd2849340c
commit e57cccc3fe
8 changed files with 85 additions and 10 deletions

View file

@ -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)
}
}

View file

@ -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")
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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")

View file

@ -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