Skip to content

Commit

Permalink
manifest: add new RawBootcImage type
Browse files Browse the repository at this point in the history
This image type is distinct from the RawOSTreeImage because the
way `bootc instal to-filesystem` works is quite different from
how our existing ostree deployments work.
  • Loading branch information
mvo5 committed Mar 18, 2024
1 parent 15cc3e0 commit 481a659
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 46 deletions.
33 changes: 19 additions & 14 deletions pkg/image/bootc_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@ import (
"math/rand"

"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/runner"
)

type BootcDiskImage struct {
*OSTreeDiskImage
Base

Platform platform.Platform
PartitionTable *disk.PartitionTable

Filename string

ContainerSource *container.SourceSpec

Compression string
}

func NewBootcDiskImage(container container.SourceSpec) *BootcDiskImage {
// XXX: hardcoded for now
ref := "ostree/1/1/0"

return &BootcDiskImage{
&OSTreeDiskImage{
Base: NewBase("bootc-raw-image"),
ContainerSource: &container,
Ref: ref,
OSName: "default",
},
Base: NewBase("bootc-raw-image"),
ContainerSource: &container,
}
}

Expand All @@ -40,14 +44,15 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes
// this is signified by passing nil to the below pipelines.
var hostPipeline manifest.Build

opts := &baseRawOstreeImageOpts{useBootupd: true}

fileBasename := img.Filename
// XXX: no support for customization right now, at least /etc/fstab
// and very basic user (root only?) should be supported
baseImage := manifest.NewRawBootcImage(buildPipeline, containers, img.Platform)
baseImage.PartitionTable = img.PartitionTable

// In BIB, we export multiple images from the same pipeline so we use the
// filename as the basename for each export and set the extensions based on
// each file format.
baseImage := baseRawOstreeImage(img.OSTreeDiskImage, buildPipeline, opts)
fileBasename := img.Filename
baseImage.SetFilename(fmt.Sprintf("%s.raw", fileBasename))

qcow2Pipeline := manifest.NewQCOW2(hostPipeline, baseImage)
Expand Down
39 changes: 8 additions & 31 deletions pkg/image/bootc_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestBootcDiskImageNew(t *testing.T) {

img := image.NewBootcDiskImage(containerSource)
require.NotNil(t, img)
assert.Equal(t, img.OSTreeDiskImage.Base.Name(), "bootc-raw-image")
assert.Equal(t, img.Base.Name(), "bootc-raw-image")
}

func makeFakeDigest(t *testing.T) string {
Expand Down Expand Up @@ -73,8 +73,8 @@ func makeBootcDiskImageOsbuildManifest(t *testing.T, opts *bootcDiskImageTestOpt
require.Nil(t, err)

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)}},
"build": []container.Spec{{Source: "some-src", Digest: makeFakeDigest(t), ImageID: makeFakeDigest(t)}},
"image": []container.Spec{{Source: "other-src", Digest: makeFakeDigest(t), ImageID: makeFakeDigest(t)}},
}

osbuildManifest, err := m.Serialize(nil, fakeSourceSpecs, nil)
Expand Down Expand Up @@ -127,39 +127,16 @@ func TestBootcDiskImageInstantiateVmdk(t *testing.T) {
require.NotNil(t, pipeline)
}

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

// check that bootupd is part of the "image" pipeline
// check that bootc.install-to-filesystem 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)
}
bootcStage := findStageFromOsbuildPipeline(t, imagePipeline, "org.osbuild.bootc.install-to-filesystem")
require.NotNil(t, bootcStage)

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

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"])
}
}
// XXX: check customizations here too
}

func TestBootcDiskImageExportPipelines(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (p Base) getCheckpoint() bool {
}

func (p *Base) Export() *artifact.Artifact {
panic("can't export pipeline")
panic("can't export pipeline directly from pipeline.Base")
}

func (p Base) getExport() bool {
Expand Down
115 changes: 115 additions & 0 deletions pkg/manifest/raw_bootc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package manifest

import (
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rpmmd"
)

// A RawBootcImage represents a raw bootc image file which can be booted in a
// hypervisor.
type RawBootcImage struct {
Base

filename string
platform platform.Platform

containers []container.SourceSpec
containerSpecs []container.Spec

// customizations go here because there is no intermediate
// tree, with `bootc install to-filesystem` we can only work
// with the image itself
PartitionTable *disk.PartitionTable
}

func (p RawBootcImage) Filename() string {
return p.filename
}

func (p *RawBootcImage) SetFilename(filename string) {
p.filename = filename
}

func NewRawBootcImage(buildPipeline Build, containers []container.SourceSpec, platform platform.Platform) *RawBootcImage {
p := &RawBootcImage{
Base: NewBase("image", buildPipeline),
filename: "disk.img",
platform: platform,

containers: containers,
}
buildPipeline.addDependent(p)
return p
}

func (p *RawBootcImage) getContainerSources() []container.SourceSpec {
return p.containers
}

func (p *RawBootcImage) getContainerSpecs() []container.Spec {
return p.containerSpecs
}

func (p *RawBootcImage) serializeStart(_ []rpmmd.PackageSpec, containerSpecs []container.Spec, _ []ostree.CommitSpec) {
if len(p.containerSpecs) > 0 {
panic("double call to serializeStart()")
}
p.containerSpecs = containerSpecs
}

func (p *RawBootcImage) serializeEnd() {
if len(p.containerSpecs) == 0 {
panic("serializeEnd() call when serialization not in progress")
}
p.containerSpecs = nil
}

func (p *RawBootcImage) serialize() osbuild.Pipeline {
pipeline := p.Base.serialize()

pt := p.PartitionTable
if pt == nil {
panic("no partition table in live image")
}

for _, stage := range osbuild.GenImagePrepareStages(pt, p.filename, osbuild.PTSfdisk) {
pipeline.AddStage(stage)
}

inputs := osbuild.ContainerDeployInputs{
Images: osbuild.NewContainersInputForSources(p.containerSpecs),
}
devices, mounts, err := osbuild.GenBootupdDevicesMounts(p.filename, p.PartitionTable)
if err != nil {
panic(err)
}
st, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
if err != nil {
panic(err)
}
pipeline.AddStage(st)

// XXX: there is no way right now to support any customizations,
// we cannot touch the filesystem after bootc installed it or
// we risk messing with it's selinux labels or future fsverity
// magic. Once we have a mechanism like --copy-etc from
// https://github.com/containers/bootc/pull/267 things should
// be a bit better

for _, stage := range osbuild.GenImageFinishStages(pt, p.filename) {
pipeline.AddStage(stage)
}

return pipeline
}

// XXX: copied from raw.go
func (p *RawBootcImage) Export() *artifact.Artifact {
p.Base.export = true
return artifact.New(p.Name(), p.Filename(), nil)
}

0 comments on commit 481a659

Please sign in to comment.