Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

many: use bootupd for image.BootcDiskImage #381

Merged
merged 1 commit into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pkg/image/bootc_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes
panic(fmt.Sprintf("no compression is allowed with %q format for %q", imgFormat, img.name))
}

baseImage := baseRawOstreeImage(img.OSTreeDiskImage, buildPipeline)
opts := &baseRawOstreeImageOpts{useBootupd: true}
baseImage := baseRawOstreeImage(img.OSTreeDiskImage, buildPipeline, opts)
switch imgFormat {
case platform.FORMAT_QCOW2:
// qcow2 runs without a build pipeline directly from "bib"
Expand Down
100 changes: 83 additions & 17 deletions pkg/image/bootc_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,24 @@ func makeFakeDigest(t *testing.T) string {
return "sha256:" + hex.EncodeToString(data[:])
}

func makeFakePlatform() platform.Platform {
type bootcDiskImageTestOpts struct {
BIOS bool
}

func makeFakePlatform(opts *bootcDiskImageTestOpts) platform.Platform {
return &platform.X86{
BasePlatform: platform.BasePlatform{
ImageFormat: platform.FORMAT_QCOW2,
},
BIOS: opts.BIOS,
}
}

func TestBootcDiskImageInstantiateNoBuildpipelineForQcow2(t *testing.T) {
func makeBootcDiskImageOsbuildManifest(t *testing.T, opts *bootcDiskImageTestOpts) manifest.OSBuildManifest {
if opts == nil {
opts = &bootcDiskImageTestOpts{}
}

containerSource := container.SourceSpec{
Source: "some-src",
Name: "name",
Expand All @@ -52,35 +61,92 @@ func TestBootcDiskImageInstantiateNoBuildpipelineForQcow2(t *testing.T) {

img := image.NewBootcDiskImage(containerSource)
require.NotNil(t, img)
img.Platform = makeFakePlatform()
img.PartitionTable = testdisk.MakeFakePartitionTable("/")
img.Platform = makeFakePlatform(opts)
img.PartitionTable = testdisk.MakeFakePartitionTable("/", "/boot", "/boot/efi")

m := &manifest.Manifest{}
runi := &runner.Fedora{}
_, err := img.InstantiateManifestFromContainers(m, containers, runi, nil)
require.Nil(t, err)
sourceSpecs := map[string][]container.Spec{

fakeSourceSpecs := map[string][]container.Spec{
"build": []container.Spec{{Source: "some-src", Digest: makeFakeDigest(t), ImageID: makeFakeDigest(t)}},
"ostree-deployment": []container.Spec{{Source: "other-src", Digest: makeFakeDigest(t), ImageID: makeFakeDigest(t)}},
}
osbuildManifest, err := m.Serialize(nil, sourceSpecs, nil)

osbuildManifest, err := m.Serialize(nil, fakeSourceSpecs, nil)
require.Nil(t, err)

return osbuildManifest
}

func findPipelineFromOsbuildManifest(t *testing.T, osbm manifest.OSBuildManifest, pipelineName string) map[string]interface{} {
var mani map[string]interface{}
err = json.Unmarshal(osbuildManifest, &mani)

err := json.Unmarshal(osbm, &mani)
require.Nil(t, err)

pipelines := mani["pipelines"].([]interface{})
findQcowStage := func() map[string]interface{} {
for _, stageIf := range pipelines {
stage := stageIf.(map[string]interface{})
if stage["name"].(string) == "qcow2" {
return stage
}
for _, pipelineIf := range pipelines {
pipeline := pipelineIf.(map[string]interface{})
if pipeline["name"].(string) == pipelineName {
return pipeline
}
}
return nil
}

func findStageFromOsbuildPipeline(t *testing.T, pipeline map[string]interface{}, stageName string) map[string]interface{} {
stages := pipeline["stages"].([]interface{})
for _, stageIf := range stages {
stage := stageIf.(map[string]interface{})
if stage["type"].(string) == stageName {
return stage
}
return nil
}
qcowStage := findQcowStage()
require.NotNil(t, qcowStage)
return nil
}

func TestBootcDiskImageInstantiateNoBuildpipelineForQcow2(t *testing.T) {
osbuildManifest := makeBootcDiskImageOsbuildManifest(t, nil)

qcowPipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "qcow2")
require.NotNil(t, qcowPipeline)
// no build pipeline for qcow2
assert.Equal(t, qcowStage["build"], nil)
assert.Equal(t, qcowPipeline["build"], nil)
}

func TestBootcDiskImageUsesBootupd(t *testing.T) {
osbuildManifest := makeBootcDiskImageOsbuildManifest(t, nil)

// check that bootupd is part of the "image" pipeline
imagePipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "image")
require.NotNil(t, imagePipeline)
bootupdStage := findStageFromOsbuildPipeline(t, imagePipeline, "org.osbuild.bootupd")
require.NotNil(t, bootupdStage)

// ensure that "grub2" is not part of the ostree pipeline
ostreeDeployPipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "ostree-deployment")
require.NotNil(t, ostreeDeployPipeline)
grubStage := findStageFromOsbuildPipeline(t, ostreeDeployPipeline, "org.osbuild.grub2")
require.Nil(t, grubStage)
}

func TestBootcDiskImageBootupdBiosSupport(t *testing.T) {
for _, withBios := range []bool{false, true} {
osbuildManifest := makeBootcDiskImageOsbuildManifest(t, &bootcDiskImageTestOpts{BIOS: withBios})

imagePipeline := findPipelineFromOsbuildManifest(t, osbuildManifest, "image")
require.NotNil(t, imagePipeline)
bootupdStage := findStageFromOsbuildPipeline(t, imagePipeline, "org.osbuild.bootupd")
require.NotNil(t, bootupdStage)

opts := bootupdStage["options"].(map[string]interface{})
if withBios {
biosOpts := opts["bios"].(map[string]interface{})
assert.Equal(t, biosOpts["device"], "disk")
} else {
require.Nil(t, opts["bios"])
}
}
}
13 changes: 11 additions & 2 deletions pkg/image/ostree_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,15 @@ func NewOSTreeDiskImageFromContainer(container container.SourceSpec, ref string)
}
}

func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline manifest.Build) *manifest.RawOSTreeImage {
type baseRawOstreeImageOpts struct {
useBootupd bool
}

func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline manifest.Build, opts *baseRawOstreeImageOpts) *manifest.RawOSTreeImage {
if opts == nil {
opts = &baseRawOstreeImageOpts{}
}

var osPipeline *manifest.OSTreeDeployment
switch {
case img.CommitSource != nil:
Expand All @@ -98,6 +106,7 @@ func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline manifest.Build) *man
osPipeline.FIPS = img.FIPS
osPipeline.IgnitionPlatform = img.IgnitionPlatform
osPipeline.LockRoot = img.LockRoot
osPipeline.UseBootupd = opts.useBootupd

// other image types (e.g. live) pass the workload to the pipeline.
if img.Workload != nil {
Expand Down Expand Up @@ -127,7 +136,7 @@ func (img *OSTreeDiskImage) InstantiateManifest(m *manifest.Manifest,
panic(fmt.Sprintf("no compression is allowed with %q format for %q", imgFormat, img.name))
}

baseImage := baseRawOstreeImage(img, buildPipeline)
baseImage := baseRawOstreeImage(img, buildPipeline, nil)
switch img.Platform.GetImageFormat() {
case platform.FORMAT_VMDK:
vmdkPipeline := manifest.NewVMDK(buildPipeline, baseImage)
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/ostree_simplified_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (img *OSTreeSimplifiedInstaller) InstantiateManifest(m *manifest.Manifest,
imageFilename := "image.raw.xz"

// image in simplified installer is always compressed
compressedImage := manifest.NewXZ(buildPipeline, baseRawOstreeImage(img.rawImage, buildPipeline))
compressedImage := manifest.NewXZ(buildPipeline, baseRawOstreeImage(img.rawImage, buildPipeline, nil))
compressedImage.SetFilename(imageFilename)

coiPipeline := manifest.NewCoreOSInstaller(
Expand Down
37 changes: 21 additions & 16 deletions pkg/manifest/ostree_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type OSTreeDeployment struct {
// Lock the root account in the deployment unless the user defined root
// user options in the build configuration.
LockRoot bool

// Use bootupd instead of grub2 as the bootloader
UseBootupd bool
}

// NewOSTreeCommitDeployment creates a pipeline for an ostree deployment from a
Expand Down Expand Up @@ -411,22 +414,24 @@ func (p *OSTreeDeployment) serialize() osbuild.Pipeline {
}
}

grubOptions := osbuild.NewGrub2StageOptions(p.PartitionTable,
strings.Join(kernelOpts, " "),
"",
p.platform.GetUEFIVendor() != "",
p.platform.GetBIOSPlatform(),
p.platform.GetUEFIVendor(), true)
grubOptions.Greenboot = true
grubOptions.Ignition = p.IgnitionPlatform != ""
grubOptions.Config = &osbuild.GRUB2Config{
Default: "saved",
Timeout: 1,
TerminalOutput: []string{"console"},
}
bootloader := osbuild.NewGRUB2Stage(grubOptions)
bootloader.MountOSTree(p.osName, ref, 0)
pipeline.AddStage(bootloader)
if !p.UseBootupd {
grubOptions := osbuild.NewGrub2StageOptions(p.PartitionTable,
strings.Join(kernelOpts, " "),
"",
p.platform.GetUEFIVendor() != "",
p.platform.GetBIOSPlatform(),
p.platform.GetUEFIVendor(), true)
grubOptions.Greenboot = true
grubOptions.Ignition = p.IgnitionPlatform != ""
grubOptions.Config = &osbuild.GRUB2Config{
Default: "saved",
Timeout: 1,
TerminalOutput: []string{"console"},
}
bootloader := osbuild.NewGRUB2Stage(grubOptions)
bootloader.MountOSTree(p.osName, ref, 0)
pipeline.AddStage(bootloader)
}

// First create custom directories, because some of the files may depend on them
if len(p.Directories) > 0 {
Expand Down
38 changes: 36 additions & 2 deletions pkg/manifest/raw_ostree.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,47 @@ func (p *RawOSTreeImage) serialize() osbuild.Pipeline {
pipeline.AddStage(stage)
}

if grubLegacy := p.treePipeline.platform.GetBIOSPlatform(); grubLegacy != "" {
pipeline.AddStage(osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(p.Filename(), pt, grubLegacy)))
if p.treePipeline.UseBootupd {
p.addBootupdStage(&pipeline)
} else {
p.maybeAddGrubInstStage(&pipeline)
}

return pipeline
}

func (p *RawOSTreeImage) addBootupdStage(pipeline *osbuild.Pipeline) {
pt := p.treePipeline.PartitionTable

treeBootupdDevices, treeBootupdMounts := osbuild.GenBootupdDevicesMounts(p.Filename(), pt)
opts := &osbuild.BootupdStageOptions{
Deployment: &osbuild.OSTreeDeployment{
OSName: p.treePipeline.osName,
Ref: p.treePipeline.ref,
},
StaticConfigs: true,
}
if legacyBios := p.treePipeline.platform.GetBIOSPlatform(); legacyBios != "" {
opts.Bios = &osbuild.BootupdStageOptionsBios{
Device: "disk",
}
}
bootupd, err := osbuild.NewBootupdStage(opts, treeBootupdDevices, treeBootupdMounts)
if err != nil {
panic(err)
}

pipeline.AddStage(bootupd)
}

func (p *RawOSTreeImage) maybeAddGrubInstStage(pipeline *osbuild.Pipeline) {
pt := p.treePipeline.PartitionTable

if grubLegacy := p.treePipeline.platform.GetBIOSPlatform(); grubLegacy != "" {
pipeline.AddStage(osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(p.Filename(), pt, grubLegacy)))
}
}

func (p *RawOSTreeImage) Export() *artifact.Artifact {
p.Base.export = true
return artifact.New(p.Name(), p.Filename(), nil)
Expand Down
17 changes: 17 additions & 0 deletions pkg/osbuild/bootupd_stage.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package osbuild
import (
"fmt"
"sort"

"github.com/osbuild/images/pkg/disk"
)

type BootupdStageOptionsBios struct {
Expand Down Expand Up @@ -73,3 +75,18 @@ func NewBootupdStage(opts *BootupdStageOptions, devices map[string]Device, mount
Mounts: mounts,
}, nil
}

func GenBootupdDevicesMounts(filename string, pt *disk.PartitionTable) (map[string]Device, []Mount) {
_, mounts, devices, err := genMountsDevicesFromPt(filename, pt)
if err != nil {
panic(err)
}
devices["disk"] = Device{
Type: "org.osbuild.loopback",
Options: &LoopbackDeviceOptions{
Filename: filename,
},
}

return devices, mounts
}
Loading