From b39d80215524900646ef57cd6e1b66dad4c3fd0e Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Mon, 23 Jan 2023 23:45:18 +0100 Subject: [PATCH] manifest: add support for selecting grub2.legacy Older OS versions (RHEL 7) with older versions of grub2 don't support BLS entries. Setting NoBLS to true configures the bootloader with traditional menu entries through the grub2.legacy osbuild stage. This requires specifying extra information for the OS to the pipeline: version, product, and nick. --- internal/distro/rhel7/images.go | 4 ++ internal/image/live.go | 9 ++++ internal/manifest/os.go | 81 +++++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/internal/distro/rhel7/images.go b/internal/distro/rhel7/images.go index 46028f354..f3295096a 100644 --- a/internal/distro/rhel7/images.go +++ b/internal/distro/rhel7/images.go @@ -182,6 +182,10 @@ func liveImage(workload workload.Workload, img.Workload = workload img.Compression = t.compression img.PartTool = osbuild.PTSgdisk // all RHEL 7 images should use sgdisk + img.NoBLS = true // RHEL 7 grub does not support BLS + img.OSProduct = t.arch.distro.product + img.OSVersion = t.arch.distro.osVersion + img.OSNick = t.arch.distro.nick // TODO: move generation into LiveImage pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) diff --git a/internal/image/live.go b/internal/image/live.go index 0dec72754..bb4843278 100644 --- a/internal/image/live.go +++ b/internal/image/live.go @@ -26,6 +26,11 @@ type LiveImage struct { Filename string Compression string PartTool osbuild.PartTool + + NoBLS bool + OSProduct string + OSVersion string + OSNick string } func NewLiveImage() *LiveImage { @@ -47,6 +52,10 @@ func (img *LiveImage) InstantiateManifest(m *manifest.Manifest, osPipeline.OSCustomizations = img.OSCustomizations osPipeline.Environment = img.Environment osPipeline.Workload = img.Workload + osPipeline.NoBLS = img.NoBLS + osPipeline.OSProduct = img.OSProduct + osPipeline.OSVersion = img.OSVersion + osPipeline.OSNick = img.OSNick imagePipeline := manifest.NewRawImage(m, buildPipeline, osPipeline) imagePipeline.PartTool = img.PartTool diff --git a/internal/manifest/os.go b/internal/manifest/os.go index f36c824c9..0088968de 100644 --- a/internal/manifest/os.go +++ b/internal/manifest/os.go @@ -130,6 +130,13 @@ type OS struct { packageSpecs []rpmmd.PackageSpec platform platform.Platform kernelVer string + + // NoBLS configures the image bootloader with traditional menu entries + // instead of BLS. Required for legacy systems like RHEL 7. + NoBLS bool + OSProduct string + OSVersion string + OSNick string } // NewOS creates a new OS pipeline. build is the build pipeline to use for @@ -287,11 +294,13 @@ func (p *OS) serialize() osbuild.Pipeline { } pipeline.AddStage(osbuild.NewRPMStage(rpmOptions, osbuild.NewRpmStageSourceFilesInputs(p.packageSpecs))) - // If the /boot is on a separate partition, the prefix for the BLS stage must be "" - if p.PartitionTable == nil || p.PartitionTable.FindMountable("/boot") == nil { - pipeline.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{})) - } else { - pipeline.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{Prefix: common.ToPtr("")})) + if !p.NoBLS { + // If the /boot is on a separate partition, the prefix for the BLS stage must be "" + if p.PartitionTable == nil || p.PartitionTable.FindMountable("/boot") == nil { + pipeline.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{})) + } else { + pipeline.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{Prefix: common.ToPtr("")})) + } } if len(p.Containers) > 0 { @@ -507,30 +516,52 @@ func (p *OS) serialize() osbuild.Pipeline { case platform.ARCH_S390X: bootloader = osbuild.NewZiplStage(new(osbuild.ZiplStageOptions)) default: - var options *osbuild.GRUB2StageOptions - options = osbuild.NewGrub2StageOptionsUnified(pt, - p.kernelVer, - p.platform.GetUEFIVendor() != "", - p.platform.GetBIOSPlatform(), - p.platform.GetUEFIVendor(), false) - if cfg := p.Grub2Config; cfg != nil { - // TODO: don't store Grub2Config in OSPipeline, making the overrides unnecessary - // grub2.Config.Default is owned and set by `NewGrub2StageOptionsUnified` - // and thus we need to preserve it - if options.Config != nil { - cfg.Default = options.Config.Default + if p.NoBLS { + // BLS entries not supported: use grub2.legacy + id := "76a22bf4-f153-4541-b6c7-0332c0dfaeac" + product := osbuild.GRUB2Product{ + Name: p.OSProduct, + Version: p.OSVersion, + Nick: p.OSNick, } - options.Config = cfg - } - if p.KernelOptionsBootloader { - options.WriteCmdLine = nil - if options.UEFI != nil { - options.UEFI.Unified = false + rescueVer, _ := rpmmd.GetVerStrFromPackageSpecList(p.packageSpecs, "dracut-config-rescue") + hasRescue := rescueVer != "" + bootloader = osbuild.NewGrub2LegacyStage( + osbuild.NewGrub2LegacyStageOptions( + p.Grub2Config, + p.PartitionTable, + kernelOptions, + p.platform.GetBIOSPlatform(), + p.platform.GetUEFIVendor(), + osbuild.MakeGrub2MenuEntries(id, p.kernelVer, product, hasRescue), + ), + ) + } else { + options := osbuild.NewGrub2StageOptionsUnified(pt, + p.kernelVer, + p.platform.GetUEFIVendor() != "", + p.platform.GetBIOSPlatform(), + p.platform.GetUEFIVendor(), false) + if cfg := p.Grub2Config; cfg != nil { + // TODO: don't store Grub2Config in OSPipeline, making the overrides unnecessary + // grub2.Config.Default is owned and set by `NewGrub2StageOptionsUnified` + // and thus we need to preserve it + if options.Config != nil { + cfg.Default = options.Config.Default + } + + options.Config = cfg } - options.KernelOptions = strings.Join(kernelOptions, " ") + if p.KernelOptionsBootloader { + options.WriteCmdLine = nil + if options.UEFI != nil { + options.UEFI.Unified = false + } + options.KernelOptions = strings.Join(kernelOptions, " ") + } + bootloader = osbuild.NewGRUB2Stage(options) } - bootloader = osbuild.NewGRUB2Stage(options) } pipeline.AddStage(bootloader)