obsuild2: generalise mkfs stage device option generation

Refactor GenMkfsStages to work with entity based methods so that it is
now able to handle all generic cases of arbitrarily nested devices.

Co-Authored-By: Christian Kellner <christian@kellner.me>
This commit is contained in:
Achilleas Koutsou 2022-02-08 22:58:51 +01:00 committed by Tom Gundersen
parent 8a73ab5980
commit 16a975fe6f
8 changed files with 122 additions and 38 deletions

View file

@ -1,5 +1,12 @@
package osbuild2
import (
"fmt"
"strings"
"github.com/osbuild/osbuild-composer/internal/disk"
)
type Devices map[string]Device
type Device struct {
@ -11,3 +18,72 @@ type Device struct {
type DeviceOptions interface {
isDeviceOptions()
}
func deviceName(p disk.Entity) string {
if p == nil {
panic("device is nil; this is a programming error")
}
switch payload := p.(type) {
case disk.Mountable:
return pathdot(payload.GetMountpoint())
case *disk.LUKSContainer:
return "luks-" + payload.UUID[:4]
case *disk.LVMVolumeGroup:
return payload.Name + "vg"
case *disk.LVMLogicalVolume:
return payload.Name
}
panic(fmt.Sprintf("unsupported device type in deviceName: '%T'", p))
}
func getDevices(path []disk.Entity, filename string) (map[string]Device, string) {
var pt *disk.PartitionTable
do := make(map[string]Device)
parent := ""
for _, elem := range path {
switch e := elem.(type) {
case *disk.PartitionTable:
pt = e
case *disk.Partition:
if pt == nil {
panic("path does not contain partition table; this is a programming error")
}
lbopt := LoopbackDeviceOptions{
Filename: filename,
Start: pt.BytesToSectors(e.Start),
Size: pt.BytesToSectors(e.Size),
SectorSize: nil,
}
name := deviceName(e.Payload)
do[name] = *NewLoopbackDevice(&lbopt)
parent = name
case *disk.LUKSContainer:
lo := LUKS2DeviceOptions{
Passphrase: e.Passphrase,
}
name := deviceName(e.Payload)
do[name] = *NewLUKS2Device(parent, &lo)
parent = name
case *disk.LVMLogicalVolume:
lo := LVM2LVDeviceOptions{
Volume: e.Name,
}
name := deviceName(e.Payload)
do[name] = *NewLVM2LVDevice(parent, &lo)
parent = name
}
}
return do, parent
}
func pathdot(path string) string {
if path == "/" {
return "root"
}
path = strings.TrimLeft(path, "/")
return strings.ReplaceAll(path, "/", ".")
}

View file

@ -8,9 +8,10 @@ type LUKS2DeviceOptions struct {
func (LUKS2DeviceOptions) isDeviceOptions() {}
func NewLUKS2Device(options *LUKS2DeviceOptions) *Device {
func NewLUKS2Device(parent string, options *LUKS2DeviceOptions) *Device {
return &Device{
Type: "org.osbuild.luks2",
Parent: parent,
Options: options,
}
}

View file

@ -7,10 +7,10 @@ type MkfsBtrfsStageOptions struct {
func (MkfsBtrfsStageOptions) isStageOptions() {}
func NewMkfsBtrfsStage(options *MkfsBtrfsStageOptions, device *Device) *Stage {
func NewMkfsBtrfsStage(options *MkfsBtrfsStageOptions, devices map[string]Device) *Stage {
return &Stage{
Type: "org.osbuild.mkfs.btrfs",
Options: options,
Devices: Devices{"device": *device},
Devices: devices,
}
}

View file

@ -7,10 +7,10 @@ type MkfsExt4StageOptions struct {
func (MkfsExt4StageOptions) isStageOptions() {}
func NewMkfsExt4Stage(options *MkfsExt4StageOptions, device *Device) *Stage {
func NewMkfsExt4Stage(options *MkfsExt4StageOptions, devices map[string]Device) *Stage {
return &Stage{
Type: "org.osbuild.mkfs.ext4",
Options: options,
Devices: Devices{"device": *device},
Devices: devices,
}
}

View file

@ -8,10 +8,10 @@ type MkfsFATStageOptions struct {
func (MkfsFATStageOptions) isStageOptions() {}
func NewMkfsFATStage(options *MkfsFATStageOptions, device *Device) *Stage {
func NewMkfsFATStage(options *MkfsFATStageOptions, devices map[string]Device) *Stage {
return &Stage{
Type: "org.osbuild.mkfs.fat",
Options: options,
Devices: Devices{"device": *device},
Devices: devices,
}
}

View file

@ -18,47 +18,50 @@ func GenMkfsStages(pt *disk.PartitionTable, device *Device) []*Stage {
panic("GenMkfsStages: failed to convert device options to loopback options")
}
for _, p := range pt.Partitions {
if p.Payload == nil {
// no filesystem for partition (e.g., BIOS boot)
continue
}
genStage := func(mnt disk.Mountable, path []disk.Entity) error {
t := mnt.GetFSType()
var stage *Stage
stageDevice := NewLoopbackDevice(
&LoopbackDeviceOptions{
Filename: devOptions.Filename,
Start: pt.BytesToSectors(p.Start),
Size: pt.BytesToSectors(p.Size),
},
)
switch p.Payload.Type {
stageDevices, lastName := getDevices(path, devOptions.Filename)
// the last device on the PartitionTable must be named "device"
lastDevice := stageDevices[lastName]
delete(stageDevices, lastName)
stageDevices["device"] = lastDevice
fsSpec := mnt.GetFSSpec()
switch t {
case "xfs":
options := &MkfsXfsStageOptions{
UUID: p.Payload.UUID,
Label: p.Payload.Label,
UUID: fsSpec.UUID,
Label: fsSpec.Label,
}
stage = NewMkfsXfsStage(options, stageDevice)
stage = NewMkfsXfsStage(options, stageDevices)
case "vfat":
options := &MkfsFATStageOptions{
VolID: strings.Replace(p.Payload.UUID, "-", "", -1),
VolID: strings.Replace(fsSpec.UUID, "-", "", -1),
}
stage = NewMkfsFATStage(options, stageDevice)
stage = NewMkfsFATStage(options, stageDevices)
case "btrfs":
options := &MkfsBtrfsStageOptions{
UUID: p.Payload.UUID,
Label: p.Payload.Label,
UUID: fsSpec.UUID,
Label: fsSpec.Label,
}
stage = NewMkfsBtrfsStage(options, stageDevice)
stage = NewMkfsBtrfsStage(options, stageDevices)
case "ext4":
options := &MkfsExt4StageOptions{
UUID: p.Payload.UUID,
Label: p.Payload.Label,
UUID: fsSpec.UUID,
Label: fsSpec.Label,
}
stage = NewMkfsExt4Stage(options, stageDevice)
stage = NewMkfsExt4Stage(options, stageDevices)
default:
panic("unknown fs type " + p.Type)
panic("unknown fs type " + t)
}
stages = append(stages, stage)
return nil
}
_ = pt.ForEachMountable(genStage) // genStage always returns nil
return stages
}

View file

@ -17,11 +17,15 @@ func TestNewMkfsStage(t *testing.T) {
}
device := NewLoopbackDevice(&devOpts)
devices := map[string]Device{
"device": *device,
}
btrfsOptions := &MkfsBtrfsStageOptions{
UUID: uuid.New().String(),
Label: "test",
}
mkbtrfs := NewMkfsBtrfsStage(btrfsOptions, device)
mkbtrfs := NewMkfsBtrfsStage(btrfsOptions, devices)
mkbtrfsExpected := &Stage{
Type: "org.osbuild.mkfs.btrfs",
Options: btrfsOptions,
@ -33,7 +37,7 @@ func TestNewMkfsStage(t *testing.T) {
UUID: uuid.New().String(),
Label: "test",
}
mkext4 := NewMkfsExt4Stage(ext4Options, device)
mkext4 := NewMkfsExt4Stage(ext4Options, devices)
mkext4Expected := &Stage{
Type: "org.osbuild.mkfs.ext4",
Options: ext4Options,
@ -46,7 +50,7 @@ func TestNewMkfsStage(t *testing.T) {
Label: "test",
FATSize: common.IntToPtr(12),
}
mkfat := NewMkfsFATStage(fatOptions, device)
mkfat := NewMkfsFATStage(fatOptions, devices)
mkfatExpected := &Stage{
Type: "org.osbuild.mkfs.fat",
Options: fatOptions,
@ -58,7 +62,7 @@ func TestNewMkfsStage(t *testing.T) {
UUID: uuid.New().String(),
Label: "test",
}
mkxfs := NewMkfsXfsStage(xfsOptions, device)
mkxfs := NewMkfsXfsStage(xfsOptions, devices)
mkxfsExpected := &Stage{
Type: "org.osbuild.mkfs.xfs",
Options: xfsOptions,

View file

@ -7,10 +7,10 @@ type MkfsXfsStageOptions struct {
func (MkfsXfsStageOptions) isStageOptions() {}
func NewMkfsXfsStage(options *MkfsXfsStageOptions, device *Device) *Stage {
func NewMkfsXfsStage(options *MkfsXfsStageOptions, devices map[string]Device) *Stage {
return &Stage{
Type: "org.osbuild.mkfs.xfs",
Options: options,
Devices: Devices{"device": *device},
Devices: devices,
}
}