debian-forge-composer/internal/osbuild2/device.go
Christian Kellner 670b936e6b osbuild2: new GenDeviceFinishStages method
This should be called at the end of the pipeline that creates an
image, to add stages that are needed to finish up the image.
Currently the only stage that will be added is the `lvm2.metadata`
stage in the case the partition layout contains LVM2.

Co-Authored-By: Achilleas Koutsou <achilleas@koutsou.net>
2022-02-22 19:23:41 +00:00

186 lines
4.5 KiB
Go

package osbuild2
import (
"fmt"
"strings"
"github.com/osbuild/osbuild-composer/internal/disk"
)
type Devices map[string]Device
type Device struct {
Type string `json:"type"`
Parent string `json:"parent,omitempty"`
Options DeviceOptions `json:"options,omitempty"`
}
type DeviceOptions interface {
isDeviceOptions()
}
func GenDeviceCreationStages(pt *disk.PartitionTable, filename string) []*Stage {
stages := make([]*Stage, 0)
genStages := func(e disk.Entity, path []disk.Entity) error {
switch ent := e.(type) {
case *disk.LUKSContainer:
// do not include us when getting the devices
stageDevices, lastName := getDevices(path[:len(path)-1], filename)
// "org.osbuild.luks2.format" expects a "device" to create the VG on,
// thus rename the last device to "device"
lastDevice := stageDevices[lastName]
delete(stageDevices, lastName)
stageDevices["device"] = lastDevice
stage := NewLUKS2CreateStage(
&LUKS2CreateStageOptions{
UUID: ent.UUID,
Passphrase: ent.Passphrase,
Cipher: ent.Cipher,
Label: ent.Label,
Subsystem: ent.Subsystem,
SectorSize: ent.SectorSize,
PBKDF: Argon2id{
Method: "argon2id",
Iterations: ent.PBKDF.Iterations,
Memory: ent.PBKDF.Memory,
Parallelism: ent.PBKDF.Parallelism,
},
},
stageDevices)
stages = append(stages, stage)
case *disk.LVMVolumeGroup:
// do not include us when getting the devices
stageDevices, lastName := getDevices(path[:len(path)-1], filename)
// "org.osbuild.lvm2.create" expects a "device" to create the VG on,
// thus rename the last device to "device"
lastDevice := stageDevices[lastName]
delete(stageDevices, lastName)
stageDevices["device"] = lastDevice
volumes := make([]LogicalVolume, len(ent.LogicalVolumes))
for idx, lv := range ent.LogicalVolumes {
volumes[idx].Name = lv.Name
volumes[idx].Size = fmt.Sprintf("%d", lv.Size)
}
stage := NewLVM2CreateStage(
&LVM2CreateStageOptions{
Volumes: volumes,
}, stageDevices)
stages = append(stages, stage)
}
return nil
}
_ = pt.ForEachEntity(genStages)
return stages
}
func GenDeviceFinishStages(pt *disk.PartitionTable, filename string) []*Stage {
stages := make([]*Stage, 0)
genStages := func(e disk.Entity, path []disk.Entity) error {
switch ent := e.(type) {
case *disk.LVMVolumeGroup:
// do not include us when getting the devices
stageDevices, lastName := getDevices(path[:len(path)-1], filename)
// "org.osbuild.lvm2.metadata" expects a "device" to rename the VG,
// thus rename the last device to "device"
lastDevice := stageDevices[lastName]
delete(stageDevices, lastName)
stageDevices["device"] = lastDevice
stage := NewLVM2MetadataStage(
&LVM2MetadataStageOptions{
VGName: ent.Name,
}, stageDevices)
stages = append(stages, stage)
}
return nil
}
_ = pt.ForEachEntity(genStages)
return stages
}
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, "/", ".")
}