Skip to content

Commit

Permalink
many: make manifest.Build an interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mvo5 committed Jan 10, 2024
1 parent 2bf1c9b commit 3fc09d7
Show file tree
Hide file tree
Showing 27 changed files with 142 additions and 96 deletions.
2 changes: 1 addition & 1 deletion pkg/image/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/osbuild/images/pkg/runner"
)

func MockManifestNewBuild(new func(m *manifest.Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts *manifest.BuildOptions) *manifest.Build) (restore func()) {
func MockManifestNewBuild(new func(m *manifest.Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts *manifest.BuildOptions) manifest.Build) (restore func()) {
saved := manifestNewBuild
manifestNewBuild = new
return func() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/ostree_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func NewOSTreeDiskImageFromContainer(container container.SourceSpec, ref string)
}
}

func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline *manifest.Build) *manifest.RawOSTreeImage {
func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline manifest.Build) *manifest.RawOSTreeImage {
var osPipeline *manifest.OSTreeDeployment
switch {
case img.CommitSource != nil:
Expand Down
2 changes: 1 addition & 1 deletion pkg/image/ostree_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestOSTreeDiskImageManifestSetsContainerBuildable(t *testing.T) {
}

var buildOpts []*manifest.BuildOptions
restore := image.MockManifestNewBuild(func(m *manifest.Manifest, r runner.Runner, repos []rpmmd.RepoConfig, opts *manifest.BuildOptions) *manifest.Build {
restore := image.MockManifestNewBuild(func(m *manifest.Manifest, r runner.Runner, repos []rpmmd.RepoConfig, opts *manifest.BuildOptions) manifest.Build {
buildOpts = append(buildOpts, opts)
return manifest.NewBuild(m, r, repos, opts)
})
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/anaconda_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type AnacondaInstaller struct {
}

func NewAnacondaInstaller(installerType AnacondaInstallerType,
buildPipeline *Build,
buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
kernelName,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/anaconda_installer_iso_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type AnacondaInstallerISOTree struct {
Files []*fsnode.File
}

func NewAnacondaInstallerISOTree(buildPipeline *Build, anacondaPipeline *AnacondaInstaller, rootfsPipeline *ISORootfsImg, bootTreePipeline *EFIBootTree) *AnacondaInstallerISOTree {
func NewAnacondaInstallerISOTree(buildPipeline Build, anacondaPipeline *AnacondaInstaller, rootfsPipeline *ISORootfsImg, bootTreePipeline *EFIBootTree) *AnacondaInstallerISOTree {

// the three pipelines should all belong to the same manifest
if anacondaPipeline.Manifest() != rootfsPipeline.Manifest() ||
Expand Down
170 changes: 107 additions & 63 deletions pkg/manifest/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,32 @@ import (
// is not predictable nor reproducible. For the purposes of building the
// build pipeline, we do use the build host's filesystem, this means we should
// make minimal assumptions about what's available there.
type Build struct {

type Build interface {
Name() string
Checkpoint()
Manifest() *Manifest

addDependent(dep Pipeline)
}

type BuildrootFromPackages struct {
Base

runner runner.Runner
dependents []Pipeline
repos []rpmmd.RepoConfig
packageSpecs []rpmmd.PackageSpec

containerBuildable bool
}

type BuildrootFromContainer struct {
Base

runner runner.Runner
dependents []Pipeline

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

Expand All @@ -37,13 +55,13 @@ type BuildOptions struct {

// NewBuild creates a new build pipeline from the repositories in repos
// and the specified packages.
func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts *BuildOptions) *Build {
func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts *BuildOptions) Build {
if opts == nil {
opts = &BuildOptions{}
}

name := "build"
pipeline := &Build{
pipeline := &BuildrootFromPackages{
Base: NewBase(name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
Expand All @@ -54,27 +72,7 @@ func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts
return pipeline
}

// NewBuildFromContainersSourceSpec creates a new build pipeline from the given
// containers specs
func NewBuildFromContainersSourceSpec(m *Manifest, runner runner.Runner, containerSources []container.SourceSpec, opts *BuildOptions) *Build {
if opts == nil {
opts = &BuildOptions{}
}

name := "build"
pipeline := &Build{
Base: NewBase(name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
containers: containerSources,

containerBuildable: opts.ContainerBuildable,
}
m.addPipeline(pipeline)
return pipeline
}

func (p *Build) addDependent(dep Pipeline) {
func (p *BuildrootFromPackages) addDependent(dep Pipeline) {
p.dependents = append(p.dependents, dep)
man := p.Manifest()
if man == nil {
Expand All @@ -83,7 +81,7 @@ func (p *Build) addDependent(dep Pipeline) {
man.addPipeline(dep)
}

func (p *Build) getPackageSetChain(distro Distro) []rpmmd.PackageSet {
func (p *BuildrootFromPackages) getPackageSetChain(distro Distro) []rpmmd.PackageSet {
// TODO: make the /usr/bin/cp dependency conditional
// TODO: make the /usr/bin/xz dependency conditional
packages := []string{
Expand All @@ -107,68 +105,43 @@ func (p *Build) getPackageSetChain(distro Distro) []rpmmd.PackageSet {
}
}

func (p *Build) getPackageSpecs() []rpmmd.PackageSpec {
func (p *BuildrootFromPackages) getPackageSpecs() []rpmmd.PackageSpec {
return p.packageSpecs
}

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

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

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

func (p *Build) serialize() osbuild.Pipeline {
if len(p.packageSpecs) == 0 && len(p.containerSpecs) == 0 {
func (p *BuildrootFromPackages) serialize() osbuild.Pipeline {
if len(p.packageSpecs) == 0 {
panic("serialization not started")
}
pipeline := p.Base.serialize()
pipeline.Runner = p.runner.String()

// XXX: should we error if both imageRef/repos are used together?
if p.repos != nil {
pipeline.AddStage(osbuild.NewRPMStage(osbuild.NewRPMStageOptions(p.repos), osbuild.NewRpmStageSourceFilesInputs(p.packageSpecs)))
pipeline.AddStage(osbuild.NewSELinuxStage(&osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
Labels: p.getSELinuxLabels(),
},
))
}
if p.containers != nil {
stage, err := osbuild.NewContainerDeployStage(osbuild.NewContainersInputForSources(p.containerSpecs))
if err != nil {
panic(err)
}
pipeline.AddStage(stage)
pipeline.AddStage(osbuild.NewSELinuxStage(
&osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
Labels: map[string]string{
"/usr/bin/ostree": "system_u:object_r:install_exec_t:s0",
},
},
))
}
pipeline.AddStage(osbuild.NewRPMStage(osbuild.NewRPMStageOptions(p.repos), osbuild.NewRpmStageSourceFilesInputs(p.packageSpecs)))
pipeline.AddStage(osbuild.NewSELinuxStage(&osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
Labels: p.getSELinuxLabels(),
}))

return pipeline
}

// Returns a map of paths to labels for the SELinux stage based on specific
// packages found in the pipeline.
func (p *Build) getSELinuxLabels() map[string]string {
func (p *BuildrootFromPackages) getSELinuxLabels() map[string]string {
labels := make(map[string]string)
for _, pkg := range p.getPackageSpecs() {
switch pkg.Name {
Expand All @@ -184,3 +157,74 @@ func (p *Build) getSELinuxLabels() map[string]string {
}
return labels
}

// NewBuildFromContainersSourceSpec creates a new build pipeline from the given
// containers specs
func NewBuildFromContainersSourceSpec(m *Manifest, runner runner.Runner, containerSources []container.SourceSpec, opts *BuildOptions) Build {
if opts == nil {
opts = &BuildOptions{}
}

name := "build"
pipeline := &BuildrootFromContainer{
Base: NewBase(name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
containers: containerSources,

containerBuildable: opts.ContainerBuildable,
}
m.addPipeline(pipeline)
return pipeline
}

func (p *BuildrootFromContainer) addDependent(dep Pipeline) {
p.dependents = append(p.dependents, dep)
man := p.Manifest()
if man == nil {
panic("cannot add build dependent without a manifest")
}
man.addPipeline(dep)
}

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

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

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

func (p *BuildrootFromContainer) serialize() osbuild.Pipeline {
if len(p.containerSpecs) == 0 {
panic("serialization not started")
}
pipeline := p.Base.serialize()
pipeline.Runner = p.runner.String()

stage, err := osbuild.NewContainerDeployStage(osbuild.NewContainersInputForSources(p.containerSpecs))
if err != nil {
panic(err)
}
pipeline.AddStage(stage)
pipeline.AddStage(osbuild.NewSELinuxStage(
&osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
Labels: map[string]string{
"/usr/bin/ostree": "system_u:object_r:install_exec_t:s0",
},
},
))

return pipeline
}
8 changes: 5 additions & 3 deletions pkg/manifest/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func TestBuildContainerBuildableNo(t *testing.T) {
mf := New()
runner := &runner.Fedora{Version: 39}

build := NewBuild(&mf, runner, repos, nil)
buildIf := NewBuild(&mf, runner, repos, nil)
build := buildIf.(*BuildrootFromPackages)
require.NotNil(t, build)

for _, tc := range []struct {
Expand Down Expand Up @@ -96,8 +97,9 @@ func TestNewBuildFromContainerSpecs(t *testing.T) {
mf := New()
runner := &runner.Fedora{Version: 39}

build := NewBuildFromContainersSourceSpec(&mf, runner, containers, nil)
require.NotNil(t, build)
buildIf := NewBuildFromContainersSourceSpec(&mf, runner, containers, nil)
require.NotNil(t, buildIf)
build := buildIf.(*BuildrootFromContainer)

fakeContainerSpecs := []container.Spec{
{
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/coi_iso_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type CoreOSISOTree struct {
}

func NewCoreOSISOTree(
buildPipeline *Build,
buildPipeline Build,
payloadPipeline *XZ,
coiPipeline *CoreOSInstaller,
bootTreePipeline *EFIBootTree) *CoreOSISOTree {
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type OSTreeCommit struct {
// NewOSTreeCommit creates a new OSTree commit pipeline. The
// treePipeline is the tree representing the content of the commit.
// ref is the ref to create the commit under.
func NewOSTreeCommit(buildPipeline *Build, treePipeline *OS, ref string) *OSTreeCommit {
func NewOSTreeCommit(buildPipeline Build, treePipeline *OS, ref string) *OSTreeCommit {
p := &OSTreeCommit{
Base: NewBase("ostree-commit", buildPipeline),
treePipeline: treePipeline,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/commit_server_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type OSTreeCommitServer struct {
// is a pipeline producing an ostree commit to be served. nginxConfigPath
// is the path to the main nginx config file and listenPort is the port
// nginx will be listening on.
func NewOSTreeCommitServer(buildPipeline *Build,
func NewOSTreeCommitServer(buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
commitPipeline *OSTreeCommit,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/coreos_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type CoreOSInstaller struct {
}

// NewCoreOSInstaller creates an CoreOS installer pipeline object.
func NewCoreOSInstaller(buildPipeline *Build,
func NewCoreOSInstaller(buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
kernelName,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/efi_boot_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type EFIBootTree struct {
KernelOpts []string
}

func NewEFIBootTree(buildPipeline *Build, product, version string) *EFIBootTree {
func NewEFIBootTree(buildPipeline Build, product, version string) *EFIBootTree {
p := &EFIBootTree{
Base: NewBase("efiboot-tree", buildPipeline),
product: product,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (p *ISO) SetFilename(filename string) {
p.filename = filename
}

func NewISO(buildPipeline *Build, treePipeline Pipeline, isoLabel string) *ISO {
func NewISO(buildPipeline Build, treePipeline Pipeline, isoLabel string) *ISO {
p := &ISO{
Base: NewBase("bootiso", buildPipeline),
treePipeline: treePipeline,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/iso_rootfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ISORootfsImg struct {
installerPipeline Pipeline
}

func NewISORootfsImg(buildPipeline *Build, installerPipeline Pipeline) *ISORootfsImg {
func NewISORootfsImg(buildPipeline Build, installerPipeline Pipeline) *ISORootfsImg {
p := &ISORootfsImg{
Base: NewBase("rootfs-image", buildPipeline),
installerPipeline: installerPipeline,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/oci_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (p *OCIContainer) SetFilename(filename string) {
p.filename = filename
}

func NewOCIContainer(buildPipeline *Build, treePipeline TreePipeline) *OCIContainer {
func NewOCIContainer(buildPipeline Build, treePipeline TreePipeline) *OCIContainer {
p := &OCIContainer{
Base: NewBase("container", buildPipeline),
treePipeline: treePipeline,
Expand Down
2 changes: 1 addition & 1 deletion pkg/manifest/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ type OS struct {
// NewOS creates a new OS pipeline. build is the build pipeline to use for
// building the OS pipeline. platform is the target platform for the final
// image. repos are the repositories to install RPMs from.
func NewOS(buildPipeline *Build, platform platform.Platform, repos []rpmmd.RepoConfig) *OS {
func NewOS(buildPipeline Build, platform platform.Platform, repos []rpmmd.RepoConfig) *OS {
name := "os"
p := &OS{
Base: NewBase(name, buildPipeline),
Expand Down
Loading

0 comments on commit 3fc09d7

Please sign in to comment.