diff --git a/internal/distro/rhel85/pipelines.go b/internal/distro/rhel85/pipelines.go index eb99b8a39..440d650c3 100644 --- a/internal/distro/rhel85/pipelines.go +++ b/internal/distro/rhel85/pipelines.go @@ -1271,16 +1271,23 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker uefi := t.supportsUEFI() legacy := t.arch.legacy - options := grub2StageOptions(partitionTable.RootFilesystem(), partitionTable.BootFilesystem(), kernelOptions, kernel, kernelVer, uefi, legacy, install) + options := osbuild.NewGrub2StageOptions(&partitionTable, kernelOptions, kernel, kernelVer, uefi, legacy, "redhat", install) options.Greenboot = greenboot + // before unifying the org.osbuild.grub2 stage option generator, we didn't + // set the following for RHEL 8.5, so we need to revert here to maintain + // the old behaviour + if uefi { + options.UEFI.Unified = false + } + return osbuild.NewGRUB2Stage(options) } func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage { platform := arch.legacy if platform != "" { - return osbuild.NewGrub2InstStage(grub2InstStageOptions(filename, pt, platform)) + return osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(filename, pt, platform)) } if arch.name == distro.S390xArchName { diff --git a/internal/distro/rhel85/stage_options.go b/internal/distro/rhel85/stage_options.go index 94caadc14..29e3ba7a1 100644 --- a/internal/distro/rhel85/stage_options.go +++ b/internal/distro/rhel85/stage_options.go @@ -5,8 +5,6 @@ import ( "os" "path/filepath" - "github.com/google/uuid" - "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/crypt" @@ -329,44 +327,6 @@ func xorrisofsStageOptions(filename string, arch string, isolinux bool) *osbuild return options } -func grub2StageOptions(rootFs *disk.Filesystem, bootFs *disk.Filesystem, kernelOptions string, - kernel *blueprint.KernelCustomization, kernelVer string, uefi bool, legacy string, install bool) *osbuild.GRUB2StageOptions { - if rootFs == nil { - panic("root filesystem must be defined for grub2 stage, this is a programming error") - } - - stageOptions := osbuild.GRUB2StageOptions{ - RootFilesystemUUID: uuid.MustParse(rootFs.UUID), - KernelOptions: kernelOptions, - Legacy: legacy, - } - - if bootFs != nil { - bootFsUUID := uuid.MustParse(bootFs.UUID) - stageOptions.BootFilesystemUUID = &bootFsUUID - } - - if uefi { - stageOptions.UEFI = &osbuild.GRUB2UEFI{ - Vendor: "redhat", - Install: install, - } - } - - if !uefi { - stageOptions.Legacy = legacy - } - - if kernel != nil { - if kernel.Append != "" { - stageOptions.KernelOptions += " " + kernel.Append - } - stageOptions.SavedEntry = "ffffffffffffffffffffffffffffffff-" + kernelVer - } - - return &stageOptions -} - // sfdiskStageOptions creates the options and devices properties for an // org.osbuild.sfdisk stage based on a partition table description func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { @@ -389,38 +349,6 @@ func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { return stageOptions } -func grub2InstStageOptions(filename string, pt *disk.PartitionTable, platform string) *osbuild.Grub2InstStageOptions { - bootPartIndex := pt.BootPartitionIndex() - if bootPartIndex == -1 { - panic("failed to find boot or root partition for grub2.inst stage") - } - bootPart := pt.Partitions[bootPartIndex] - prefixPath := "/boot/grub2" - if bootPart.Payload.Mountpoint == "/boot" { - prefixPath = "/grub2" - } - core := osbuild.CoreMkImage{ - Type: "mkimage", - PartLabel: pt.Type, - Filesystem: pt.Partitions[bootPartIndex].Payload.Type, - } - - prefix := osbuild.PrefixPartition{ - Type: "partition", - PartLabel: pt.Type, - Number: uint(bootPartIndex), - Path: prefixPath, - } - - return &osbuild.Grub2InstStageOptions{ - Filename: filename, - Platform: platform, - Location: pt.BytesToSectors(pt.Partitions[0].Start), - Core: core, - Prefix: prefix, - } -} - func ziplInstStageOptions(kernel string, pt *disk.PartitionTable) *osbuild.ZiplInstStageOptions { bootPartIndex := pt.BootPartitionIndex() if bootPartIndex == -1 { diff --git a/internal/distro/rhel86/pipelines.go b/internal/distro/rhel86/pipelines.go index b667f2bb2..14970ade5 100644 --- a/internal/distro/rhel86/pipelines.go +++ b/internal/distro/rhel86/pipelines.go @@ -995,16 +995,23 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker uefi := t.supportsUEFI() legacy := t.arch.legacy - options := grub2StageOptions(partitionTable.RootFilesystem(), partitionTable.BootFilesystem(), kernelOptions, kernel, kernelVer, uefi, legacy, t.arch.distro.vendor, install) + options := osbuild.NewGrub2StageOptions(&partitionTable, kernelOptions, kernel, kernelVer, uefi, legacy, t.arch.distro.vendor, install) options.Greenboot = greenboot + // before unifying the org.osbuild.grub2 stage option generator, we didn't + // set the following for RHEL 8.5, so we need to revert here to maintain + // the old behaviour + if uefi { + options.UEFI.Unified = false + } + return osbuild.NewGRUB2Stage(options) } func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage { platform := arch.legacy if platform != "" { - return osbuild.NewGrub2InstStage(grub2InstStageOptions(filename, pt, platform)) + return osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(filename, pt, platform)) } if arch.name == distro.S390xArchName { diff --git a/internal/distro/rhel86/stage_options.go b/internal/distro/rhel86/stage_options.go index 92cbd5f74..9779b1184 100644 --- a/internal/distro/rhel86/stage_options.go +++ b/internal/distro/rhel86/stage_options.go @@ -5,8 +5,6 @@ import ( "os" "path/filepath" - "github.com/google/uuid" - "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/crypt" @@ -363,51 +361,6 @@ func xorrisofsStageOptions(filename, isolabel, arch string, isolinux bool) *osbu return options } -func grub2StageOptions(rootFs *disk.Filesystem, - bootFs *disk.Filesystem, - kernelOptions string, - kernel *blueprint.KernelCustomization, - kernelVer string, - uefi bool, - legacy string, - vendor string, - install bool) *osbuild.GRUB2StageOptions { - if rootFs == nil { - panic("root partition must be defined for grub2 stage, this is a programming error") - } - - stageOptions := osbuild.GRUB2StageOptions{ - RootFilesystemUUID: uuid.MustParse(rootFs.UUID), - KernelOptions: kernelOptions, - Legacy: legacy, - } - - if bootFs != nil { - bootFsUUID := uuid.MustParse(bootFs.UUID) - stageOptions.BootFilesystemUUID = &bootFsUUID - } - - if uefi { - stageOptions.UEFI = &osbuild.GRUB2UEFI{ - Vendor: vendor, - Install: install, - } - } - - if !uefi { - stageOptions.Legacy = legacy - } - - if kernel != nil { - if kernel.Append != "" { - stageOptions.KernelOptions += " " + kernel.Append - } - stageOptions.SavedEntry = "ffffffffffffffffffffffffffffffff-" + kernelVer - } - - return &stageOptions -} - // sfdiskStageOptions creates the options and devices properties for an // org.osbuild.sfdisk stage based on a partition table description func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { @@ -430,38 +383,6 @@ func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { return stageOptions } -func grub2InstStageOptions(filename string, pt *disk.PartitionTable, platform string) *osbuild.Grub2InstStageOptions { - bootPartIndex := pt.BootPartitionIndex() - if bootPartIndex == -1 { - panic("failed to find boot or root partition for grub2.inst stage") - } - bootPart := pt.Partitions[bootPartIndex] - prefixPath := "/boot/grub2" - if bootPart.Payload.Mountpoint == "/boot" { - prefixPath = "/grub2" - } - core := osbuild.CoreMkImage{ - Type: "mkimage", - PartLabel: pt.Type, - Filesystem: pt.Partitions[bootPartIndex].Payload.Type, - } - - prefix := osbuild.PrefixPartition{ - Type: "partition", - PartLabel: pt.Type, - Number: uint(bootPartIndex), - Path: prefixPath, - } - - return &osbuild.Grub2InstStageOptions{ - Filename: filename, - Platform: platform, - Location: pt.BytesToSectors(pt.Partitions[0].Start), - Core: core, - Prefix: prefix, - } -} - func ziplInstStageOptions(kernel string, pt *disk.PartitionTable) *osbuild.ZiplInstStageOptions { bootPartIndex := pt.BootPartitionIndex() if bootPartIndex == -1 { diff --git a/internal/distro/rhel90/pipelines.go b/internal/distro/rhel90/pipelines.go index ee692653f..0ba621278 100644 --- a/internal/distro/rhel90/pipelines.go +++ b/internal/distro/rhel90/pipelines.go @@ -991,7 +991,7 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker uefi := t.supportsUEFI() legacy := t.arch.legacy - options := grub2StageOptions(partitionTable.RootFilesystem(), partitionTable.BootFilesystem(), kernelOptions, kernel, kernelVer, uefi, legacy, t.arch.distro.vendor, install) + options := osbuild.NewGrub2StageOptions(&partitionTable, kernelOptions, kernel, kernelVer, uefi, legacy, t.arch.distro.vendor, install) options.Greenboot = greenboot return osbuild.NewGRUB2Stage(options) @@ -1000,7 +1000,7 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage { platform := arch.legacy if platform != "" { - return osbuild.NewGrub2InstStage(grub2InstStageOptions(filename, pt, platform)) + return osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(filename, pt, platform)) } if arch.name == distro.S390xArchName { diff --git a/internal/distro/rhel90/stage_options.go b/internal/distro/rhel90/stage_options.go index ca4f7c84e..d67874a56 100644 --- a/internal/distro/rhel90/stage_options.go +++ b/internal/distro/rhel90/stage_options.go @@ -5,8 +5,6 @@ import ( "os" "path/filepath" - "github.com/google/uuid" - "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/crypt" @@ -363,52 +361,6 @@ func xorrisofsStageOptions(filename, isolabel, arch string, isolinux bool) *osbu return options } -func grub2StageOptions(rootFs *disk.Filesystem, - bootFs *disk.Filesystem, - kernelOptions string, - kernel *blueprint.KernelCustomization, - kernelVer string, - uefi bool, - legacy string, - vendor string, - install bool) *osbuild.GRUB2StageOptions { - if rootFs == nil { - panic("root filesystem must be defined for grub2 stage, this is a programming error") - } - - stageOptions := osbuild.GRUB2StageOptions{ - RootFilesystemUUID: uuid.MustParse(rootFs.UUID), - KernelOptions: kernelOptions, - Legacy: legacy, - } - - if bootFs != nil { - bootFsUUID := uuid.MustParse(bootFs.UUID) - stageOptions.BootFilesystemUUID = &bootFsUUID - } - - if uefi { - stageOptions.UEFI = &osbuild.GRUB2UEFI{ - Vendor: vendor, - Install: install, - Unified: legacy == "", // force unified grub scheme for pure efi systems - } - } - - if !uefi { - stageOptions.Legacy = legacy - } - - if kernel != nil { - if kernel.Append != "" { - stageOptions.KernelOptions += " " + kernel.Append - } - stageOptions.SavedEntry = "ffffffffffffffffffffffffffffffff-" + kernelVer - } - - return &stageOptions -} - // sfdiskStageOptions creates the options and devices properties for an // org.osbuild.sfdisk stage based on a partition table description func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { @@ -431,38 +383,6 @@ func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { return stageOptions } -func grub2InstStageOptions(filename string, pt *disk.PartitionTable, platform string) *osbuild.Grub2InstStageOptions { - bootPartIndex := pt.BootPartitionIndex() - if bootPartIndex == -1 { - panic("failed to find boot or root partition for grub2.inst stage") - } - bootPart := pt.Partitions[bootPartIndex] - prefixPath := "/boot/grub2" - if bootPart.Payload.Mountpoint == "/boot" { - prefixPath = "/grub2" - } - core := osbuild.CoreMkImage{ - Type: "mkimage", - PartLabel: pt.Type, - Filesystem: pt.Partitions[bootPartIndex].Payload.Type, - } - - prefix := osbuild.PrefixPartition{ - Type: "partition", - PartLabel: pt.Type, - Number: uint(bootPartIndex), - Path: prefixPath, - } - - return &osbuild.Grub2InstStageOptions{ - Filename: filename, - Platform: platform, - Location: pt.BytesToSectors(pt.Partitions[0].Start), - Core: core, - Prefix: prefix, - } -} - func ziplInstStageOptions(kernel string, pt *disk.PartitionTable) *osbuild.ZiplInstStageOptions { bootPartIndex := pt.BootPartitionIndex() if bootPartIndex == -1 { diff --git a/internal/distro/rhel90beta/pipelines.go b/internal/distro/rhel90beta/pipelines.go index 6d21a4152..c9d3394bd 100644 --- a/internal/distro/rhel90beta/pipelines.go +++ b/internal/distro/rhel90beta/pipelines.go @@ -1081,13 +1081,13 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker kernelOptions := t.kernelOptions uefi := t.supportsUEFI() legacy := t.arch.legacy - return osbuild.NewGRUB2Stage(grub2StageOptions(partitionTable.RootFilesystem(), partitionTable.BootFilesystem(), kernelOptions, kernel, kernelVer, uefi, legacy)) + return osbuild.NewGRUB2Stage(osbuild.NewGrub2StageOptions(&partitionTable, kernelOptions, kernel, kernelVer, uefi, legacy, "redhat", false)) } func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage { platform := arch.legacy if platform != "" { - return osbuild.NewGrub2InstStage(grub2InstStageOptions(filename, pt, platform)) + return osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(filename, pt, platform)) } if arch.name == distro.S390xArchName { diff --git a/internal/distro/rhel90beta/stage_options.go b/internal/distro/rhel90beta/stage_options.go index 2c8ccc26b..a573cc5b9 100644 --- a/internal/distro/rhel90beta/stage_options.go +++ b/internal/distro/rhel90beta/stage_options.go @@ -4,8 +4,6 @@ import ( "fmt" "path/filepath" - "github.com/google/uuid" - "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/crypt" "github.com/osbuild/osbuild-composer/internal/disk" @@ -308,44 +306,6 @@ func xorrisofsStageOptions(filename string, arch string) *osbuild.XorrisofsStage } } -func grub2StageOptions(rootFs *disk.Filesystem, bootFs *disk.Filesystem, kernelOptions string, - kernel *blueprint.KernelCustomization, kernelVer string, uefi bool, legacy string) *osbuild.GRUB2StageOptions { - if rootFs == nil { - panic("root filesystem must be defined for grub2 stage, this is a programming error") - } - - stageOptions := osbuild.GRUB2StageOptions{ - RootFilesystemUUID: uuid.MustParse(rootFs.UUID), - KernelOptions: kernelOptions, - Legacy: legacy, - } - - if bootFs != nil { - bootFsUUID := uuid.MustParse(bootFs.UUID) - stageOptions.BootFilesystemUUID = &bootFsUUID - } - - if uefi { - stageOptions.UEFI = &osbuild.GRUB2UEFI{ - Vendor: "redhat", - Unified: legacy == "", // force unified grub scheme for pure efi systems - } - } - - if !uefi { - stageOptions.Legacy = legacy - } - - if kernel != nil { - if kernel.Append != "" { - stageOptions.KernelOptions += " " + kernel.Append - } - stageOptions.SavedEntry = "ffffffffffffffffffffffffffffffff-" + kernelVer - } - - return &stageOptions -} - // sfdiskStageOptions creates the options and devices properties for an // org.osbuild.sfdisk stage based on a partition table description func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { @@ -368,33 +328,6 @@ func sfdiskStageOptions(pt *disk.PartitionTable) *osbuild.SfdiskStageOptions { return stageOptions } -func grub2InstStageOptions(filename string, pt *disk.PartitionTable, platform string) *osbuild.Grub2InstStageOptions { - bootPartIndex := pt.BootPartitionIndex() - if bootPartIndex == -1 { - panic("failed to find boot or root partition for grub2.inst stage") - } - core := osbuild.CoreMkImage{ - Type: "mkimage", - PartLabel: pt.Type, - Filesystem: pt.Partitions[bootPartIndex].Payload.Type, - } - - prefix := osbuild.PrefixPartition{ - Type: "partition", - PartLabel: pt.Type, - Number: uint(bootPartIndex), - Path: "/boot/grub2", - } - - return &osbuild.Grub2InstStageOptions{ - Filename: filename, - Platform: platform, - Location: pt.BytesToSectors(pt.Partitions[0].Start), - Core: core, - Prefix: prefix, - } -} - func ziplInstStageOptions(kernel string, pt *disk.PartitionTable) *osbuild.ZiplInstStageOptions { bootPartIndex := pt.BootPartitionIndex() if bootPartIndex == -1 { diff --git a/internal/osbuild2/grub2_inst_stage.go b/internal/osbuild2/grub2_inst_stage.go index fdd4a4c46..c7a65dd38 100644 --- a/internal/osbuild2/grub2_inst_stage.go +++ b/internal/osbuild2/grub2_inst_stage.go @@ -3,6 +3,8 @@ package osbuild2 import ( "encoding/json" "fmt" + + "github.com/osbuild/osbuild-composer/internal/disk" ) // Install the grub2 boot loader for non-UEFI systems or hybrid boot @@ -92,3 +94,58 @@ func (options Grub2InstStageOptions) MarshalJSON() ([]byte, error) { return json.Marshal(g2options) } + +func NewGrub2InstStageOption(filename string, pt *disk.PartitionTable, platform string) *Grub2InstStageOptions { + bootIdx := -1 + rootIdx := -1 + for idx := range pt.Partitions { + // NOTE: we only support having /boot at the top level of the partition + // table (e.g., not in LUKS or LVM), so we don't need to descend into + // VolumeContainer types. If /boot is on the root partition, then the + // root partition needs to be at the top level. + partition := &pt.Partitions[idx] + if partition.Payload == nil { + continue + } + if partition.Payload.GetMountpoint() == "/boot" { + bootIdx = idx + } else if partition.Payload.GetMountpoint() == "/" { + rootIdx = idx + } + } + if bootIdx == -1 { + // if there's no boot partition, fall back to root + if rootIdx == -1 { + // no root either!? + panic("failed to find boot or root partition for grub2.inst stage") + } + bootIdx = rootIdx + } + + bootPart := pt.Partitions[bootIdx] + bootPayload := bootPart.Payload + prefixPath := "/boot/grub2" + if bootPayload.GetMountpoint() == "/boot" { + prefixPath = "/grub2" + } + core := CoreMkImage{ + Type: "mkimage", + PartLabel: pt.Type, + Filesystem: bootPayload.GetFSType(), + } + + prefix := PrefixPartition{ + Type: "partition", + PartLabel: pt.Type, + Number: uint(bootIdx), + Path: prefixPath, + } + + return &Grub2InstStageOptions{ + Filename: filename, + Platform: platform, + Location: pt.BytesToSectors(pt.Partitions[0].Start), + Core: core, + Prefix: prefix, + } +} diff --git a/internal/osbuild2/grub2_stage.go b/internal/osbuild2/grub2_stage.go index c2f9762b9..df49a255a 100644 --- a/internal/osbuild2/grub2_stage.go +++ b/internal/osbuild2/grub2_stage.go @@ -1,6 +1,10 @@ package osbuild2 -import "github.com/google/uuid" +import ( + "github.com/google/uuid" + "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/disk" +) // The GRUB2StageOptions describes the bootloader configuration. // @@ -35,3 +39,66 @@ func NewGRUB2Stage(options *GRUB2StageOptions) *Stage { Options: options, } } + +func NewGrub2StageOptions(pt *disk.PartitionTable, + kernelOptions string, + kernel *blueprint.KernelCustomization, + kernelVer string, + uefi bool, + legacy string, + vendor string, + install bool) *GRUB2StageOptions { + + var bootFs, rootFs disk.Mountable + for idx := range pt.Partitions { + // NOTE: we only support having /boot at the top level of the partition + // table (e.g., not in LUKS or LVM), so we don't need to descend into + // VolumeContainer types. If /boot is on the root partition, then the + // root partition needs to be at the top level. + partition := &pt.Partitions[idx] + if partition.Payload == nil { + continue + } + if partition.Payload.GetMountpoint() == "/boot" { + bootFs = partition.Payload + } else if partition.Payload.GetMountpoint() == "/" { + rootFs = partition.Payload + } + } + + if rootFs == nil { + panic("root filesystem must be defined for grub2 stage, this is a programming error") + } + + stageOptions := GRUB2StageOptions{ + RootFilesystemUUID: uuid.MustParse(rootFs.GetFSSpec().UUID), + KernelOptions: kernelOptions, + Legacy: legacy, + } + + if bootFs != nil { + bootFsUUID := uuid.MustParse(bootFs.GetFSSpec().UUID) + stageOptions.BootFilesystemUUID = &bootFsUUID + } + + if uefi { + stageOptions.UEFI = &GRUB2UEFI{ + Vendor: vendor, + Install: install, + Unified: legacy == "", // force unified grub scheme for pure efi systems + } + } + + if !uefi { + stageOptions.Legacy = legacy + } + + if kernel != nil { + if kernel.Append != "" { + stageOptions.KernelOptions += " " + kernel.Append + } + stageOptions.SavedEntry = "ffffffffffffffffffffffffffffffff-" + kernelVer + } + + return &stageOptions +}