Port osbuild/images v0.33.0 with dot-notation to composer

Update the osbuild/images to the version which introduces "dot notation"
for distro release versions.

 - Replace all uses of distroregistry by distrofactory.
 - Delete local version of reporegistry and use the one from the
   osbuild/images.
 - Weldr: unify `createWeldrAPI()` and `createWeldrAPI2()` into a single
   `createTestWeldrAPI()` function`.
 - store/fixture: rework fixtures to allow overriding the host distro
   name and host architecture name. A cleanup function to restore the
   host distro and arch names is always part of the fixture struct.
 - Delete `distro_mock` package, since it is no longer used.
 - Bump the required version of osbuild to 98, because the OSCAP
   customization is using the 'compress_results' stage option, which is
   not available in older versions of osbuild.

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2024-01-08 17:58:49 +01:00 committed by Achilleas Koutsou
parent f6ff8c40dd
commit 625b1578fa
1166 changed files with 154457 additions and 5508 deletions

View file

@ -72,11 +72,13 @@ type AnacondaInstaller struct {
AdditionalDrivers []string
Files []*fsnode.File
// Temporary
UseRHELLoraxTemplates bool
}
func NewAnacondaInstaller(m *Manifest,
installerType AnacondaInstallerType,
buildPipeline *Build,
func NewAnacondaInstaller(installerType AnacondaInstallerType,
buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
kernelName,
@ -84,7 +86,7 @@ func NewAnacondaInstaller(m *Manifest,
version string) *AnacondaInstaller {
name := "anaconda-tree"
p := &AnacondaInstaller{
Base: NewBase(m, name, buildPipeline),
Base: NewBase(name, buildPipeline),
Type: installerType,
platform: platform,
repos: filterRepos(repos, name),
@ -93,7 +95,6 @@ func NewAnacondaInstaller(m *Manifest,
version: version,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}
@ -135,8 +136,18 @@ func (p *AnacondaInstaller) getBuildPackages(Distro) []string {
packages := p.anacondaBootPackageSet()
packages = append(packages,
"rpm",
"lorax-templates-generic",
)
if p.UseRHELLoraxTemplates {
packages = append(packages,
"lorax-templates-rhel",
)
} else {
packages = append(packages,
"lorax-templates-generic",
)
}
return packages
}
@ -269,9 +280,17 @@ func (p *AnacondaInstaller) serialize() osbuild.Pipeline {
}
if p.Type == AnacondaInstallerTypePayload {
var LoraxPath string
if p.UseRHELLoraxTemplates {
LoraxPath = "80-rhel/runtime-postinstall.tmpl"
} else {
LoraxPath = "99-generic/runtime-postinstall.tmpl"
}
pipeline.AddStage(osbuild.NewAnacondaStage(osbuild.NewAnacondaStageOptions(p.AdditionalAnacondaModules)))
pipeline.AddStage(osbuild.NewLoraxScriptStage(&osbuild.LoraxScriptStageOptions{
Path: "99-generic/runtime-postinstall.tmpl",
Path: LoraxPath,
BaseArch: p.platform.GetArch().String(),
}))
}

View file

@ -5,6 +5,7 @@ import (
"path"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/osbuild"
@ -22,6 +23,7 @@ type AnacondaInstallerISOTree struct {
// TODO: review optional and mandatory fields and their meaning
OSName string
Release string
Remote string
Users []users.User
Groups []users.Group
@ -37,7 +39,7 @@ type AnacondaInstallerISOTree struct {
// Anaconda pipeline.
KSPath string
// The path where the payload (tarball or ostree repo) will be stored.
// The path where the payload (tarball, ostree repo, or container) will be stored.
PayloadPath string
isoLabel string
@ -48,14 +50,18 @@ type AnacondaInstallerISOTree struct {
OSTreeCommitSource *ostree.SourceSpec
ostreeCommitSpec *ostree.CommitSpec
ContainerSource *container.SourceSpec
containerSpec *container.Spec
KernelOpts []string
// Enable ISOLinux stage
ISOLinux bool
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() ||
@ -63,14 +69,13 @@ func NewAnacondaInstallerISOTree(buildPipeline *Build, anacondaPipeline *Anacond
panic("pipelines from different manifests")
}
p := &AnacondaInstallerISOTree{
Base: NewBase(anacondaPipeline.Manifest(), "bootiso-tree", buildPipeline),
Base: NewBase("bootiso-tree", buildPipeline),
anacondaPipeline: anacondaPipeline,
rootfsPipeline: rootfsPipeline,
bootTreePipeline: bootTreePipeline,
isoLabel: bootTreePipeline.ISOLabel,
}
buildPipeline.addDependent(p)
anacondaPipeline.Manifest().addPipeline(p)
return p
}
@ -91,6 +96,32 @@ func (p *AnacondaInstallerISOTree) getOSTreeCommits() []ostree.CommitSpec {
return []ostree.CommitSpec{*p.ostreeCommitSpec}
}
func (p *AnacondaInstallerISOTree) getContainerSpecs() []container.Spec {
if p.containerSpec == nil {
return []container.Spec{}
}
return []container.Spec{*p.containerSpec}
}
func (p *AnacondaInstallerISOTree) getContainerSources() []container.SourceSpec {
if p.ContainerSource == nil {
return []container.SourceSpec{}
}
return []container.SourceSpec{
*p.ContainerSource,
}
}
func (p *AnacondaInstallerISOTree) getInline() []string {
inlineData := []string{}
// inline data for custom files
for _, file := range p.Files {
inlineData = append(inlineData, string(file.Data()))
}
return inlineData
}
func (p *AnacondaInstallerISOTree) getBuildPackages(_ Distro) []string {
packages := []string{
"squashfs-tools",
@ -100,6 +131,10 @@ func (p *AnacondaInstallerISOTree) getBuildPackages(_ Distro) []string {
packages = append(packages, "rpm-ostree")
}
if p.ContainerSource != nil {
packages = append(packages, "skopeo")
}
if p.OSPipeline != nil {
packages = append(packages, "tar")
}
@ -107,33 +142,57 @@ func (p *AnacondaInstallerISOTree) getBuildPackages(_ Distro) []string {
return packages
}
func (p *AnacondaInstallerISOTree) serializeStart(_ []rpmmd.PackageSpec, _ []container.Spec, commits []ostree.CommitSpec) {
if len(commits) == 0 {
// nothing to do
return
func (p *AnacondaInstallerISOTree) serializeStart(_ []rpmmd.PackageSpec, containers []container.Spec, commits []ostree.CommitSpec) {
if p.ostreeCommitSpec != nil || p.containerSpec != nil {
panic("double call to serializeStart()")
}
if len(commits) > 1 {
panic("pipeline supports at most one ostree commit")
}
p.ostreeCommitSpec = &commits[0]
if len(containers) > 1 {
panic("pipeline supports at most one container")
}
if len(commits) > 0 {
p.ostreeCommitSpec = &commits[0]
}
if len(containers) > 0 {
p.containerSpec = &containers[0]
}
}
func (p *AnacondaInstallerISOTree) serializeEnd() {
p.ostreeCommitSpec = nil
p.containerSpec = nil
}
func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline {
// If the anaconda pipeline is a payload then we need one of two payload types
// If the anaconda pipeline is a payload then we need one of three payload types
if p.anacondaPipeline.Type == AnacondaInstallerTypePayload {
if p.ostreeCommitSpec == nil && p.OSPipeline == nil {
panic("missing ostree or ospipeline parameters in ISO tree pipeline")
count := 0
if p.ostreeCommitSpec != nil {
count++
}
// But not both payloads
if p.ostreeCommitSpec != nil && p.OSPipeline != nil {
panic("got both ostree and ospipeline parameters in ISO tree pipeline")
if p.containerSpec != nil {
count++
}
if p.OSPipeline != nil {
count++
}
if count == 0 {
panic("missing ostree, container, or ospipeline parameters in ISO tree pipeline")
}
// But not more than one payloads
if count > 1 {
panic("got multiple payloads in ISO tree pipeline")
}
}
@ -278,6 +337,7 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline {
p.Groups,
makeISORootPath(p.PayloadPath),
p.ostreeCommitSpec.Ref,
p.Remote,
p.OSName)
if err != nil {
@ -287,6 +347,69 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline {
pipeline.AddStage(osbuild.NewKickstartStage(kickstartOptions))
}
if p.containerSpec != nil {
images := osbuild.NewContainersInputForSources([]container.Spec{*p.containerSpec})
pipeline.AddStage(osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
Paths: []osbuild.MkdirStagePath{
{
Path: p.PayloadPath,
},
},
}))
// copy the container in
pipeline.AddStage(osbuild.NewSkopeoStageWithOCI(
p.PayloadPath,
images,
nil))
// do what we can in our kickstart stage
kickstartOptions, err := osbuild.NewKickstartStageOptionsWithOSTreeContainer(
"/osbuild-base.ks",
p.Users,
p.Groups,
path.Join("/run/install/repo", p.PayloadPath),
"oci",
"",
"")
if err != nil {
panic("failed to create kickstartstage options")
}
pipeline.AddStage(osbuild.NewKickstartStage(kickstartOptions))
// and what we can't do in a separate kickstart that we include
kickstartFile, err := fsnode.NewFile(p.KSPath, nil, nil, nil, []byte(`
%include /run/install/repo/osbuild-base.ks
rootpw --lock
lang en_US.UTF-8
keyboard us
timezone UTC
clearpart --all
reqpart --add-boot
part swap --fstype=swap --size=1024
part / --fstype=ext4 --grow
reboot --eject
`))
if err != nil {
panic(err)
}
p.Files = []*fsnode.File{kickstartFile}
pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...)
}
if p.OSPipeline != nil {
// Create the payload tarball
pipeline.AddStage(osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: p.PayloadPath}, p.OSPipeline.name))

View file

@ -15,7 +15,16 @@ 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
@ -34,29 +43,33 @@ 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{
Base: NewBase(m, name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
repos: filterRepos(repos, name),
pipeline := &BuildrootFromPackages{
Base: NewBase(name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
repos: filterRepos(repos, name),
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 {
panic("cannot add build dependent without a manifest")
}
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{
@ -80,25 +93,25 @@ 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) serializeStart(packages []rpmmd.PackageSpec, _ []container.Spec, _ []ostree.CommitSpec) {
func (p *BuildrootFromPackages) serializeStart(packages []rpmmd.PackageSpec, _ []container.Spec, _ []ostree.CommitSpec) {
if len(p.packageSpecs) > 0 {
panic("double call to serializeStart()")
}
p.packageSpecs = packages
}
func (p *Build) serializeEnd() {
func (p *BuildrootFromPackages) serializeEnd() {
if len(p.packageSpecs) == 0 {
panic("serializeEnd() call when serialization not in progress")
}
p.packageSpecs = nil
}
func (p *Build) serialize() osbuild.Pipeline {
func (p *BuildrootFromPackages) serialize() osbuild.Pipeline {
if len(p.packageSpecs) == 0 {
panic("serialization not started")
}
@ -117,7 +130,7 @@ func (p *Build) serialize() osbuild.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 {
@ -133,3 +146,103 @@ func (p *Build) getSELinuxLabels() map[string]string {
}
return labels
}
type BuildrootFromContainer struct {
Base
runner runner.Runner
dependents []Pipeline
containers []container.SourceSpec
containerSpecs []container.Spec
containerBuildable bool
}
// NewBuildFromContainer creates a new build pipeline from the given
// containers specs
func NewBuildFromContainer(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) getContainerSpecs() []container.Spec {
return p.containerSpecs
}
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) getSELinuxLabels() map[string]string {
labels := map[string]string{
"/usr/bin/ostree": "system_u:object_r:install_exec_t:s0",
}
if p.containerBuildable {
labels["/usr/bin/mount"] = "system_u:object_r:install_exec_t:s0"
labels["/usr/bin/umount"] = "system_u:object_r:install_exec_t:s0"
}
return labels
}
func (p *BuildrootFromContainer) serialize() osbuild.Pipeline {
if len(p.containerSpecs) == 0 {
panic("serialization not started")
}
pipeline := p.Base.serialize()
pipeline.Runner = p.runner.String()
inputs := osbuild.NewContainersInputForSources(p.containerSpecs)
options := &osbuild.ContainerDeployOptions{
Exclude: []string{"/sysroot"},
}
stage, err := osbuild.NewContainerDeployStage(inputs, options)
if err != nil {
panic(err)
}
pipeline.AddStage(stage)
pipeline.AddStage(osbuild.NewSELinuxStage(
&osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
Labels: p.getSELinuxLabels(),
},
))
return pipeline
}

View file

@ -36,7 +36,7 @@ type CoreOSISOTree struct {
}
func NewCoreOSISOTree(
buildPipeline *Build,
buildPipeline Build,
payloadPipeline *XZ,
coiPipeline *CoreOSInstaller,
bootTreePipeline *EFIBootTree) *CoreOSISOTree {
@ -48,14 +48,13 @@ func NewCoreOSISOTree(
}
p := &CoreOSISOTree{
Base: NewBase(coiPipeline.Manifest(), "bootiso-tree", buildPipeline),
Base: NewBase("bootiso-tree", buildPipeline),
payloadPipeline: payloadPipeline,
coiPipeline: coiPipeline,
bootTreePipeline: bootTreePipeline,
isoLabel: bootTreePipeline.ISOLabel,
}
buildPipeline.addDependent(p)
coiPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -16,14 +16,13 @@ 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(treePipeline.Manifest(), "ostree-commit", buildPipeline),
Base: NewBase("ostree-commit", buildPipeline),
treePipeline: treePipeline,
ref: ref,
}
buildPipeline.addDependent(p)
treePipeline.Manifest().addPipeline(p)
return p
}

View file

@ -36,8 +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(m *Manifest,
buildPipeline *Build,
func NewOSTreeCommitServer(buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
commitPipeline *OSTreeCommit,
@ -45,7 +44,7 @@ func NewOSTreeCommitServer(m *Manifest,
listenPort string) *OSTreeCommitServer {
name := "container-tree"
p := &OSTreeCommitServer{
Base: NewBase(m, name, buildPipeline),
Base: NewBase(name, buildPipeline),
platform: platform,
repos: filterRepos(repos, name),
commitPipeline: commitPipeline,
@ -53,11 +52,7 @@ func NewOSTreeCommitServer(m *Manifest,
listenPort: listenPort,
Language: "en_US",
}
if commitPipeline.Base.manifest != m {
panic("commit pipeline from different manifest")
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}

View file

@ -47,8 +47,7 @@ type CoreOSInstaller struct {
}
// NewCoreOSInstaller creates an CoreOS installer pipeline object.
func NewCoreOSInstaller(m *Manifest,
buildPipeline *Build,
func NewCoreOSInstaller(buildPipeline Build,
platform platform.Platform,
repos []rpmmd.RepoConfig,
kernelName,
@ -56,7 +55,7 @@ func NewCoreOSInstaller(m *Manifest,
version string) *CoreOSInstaller {
name := "coi-tree"
p := &CoreOSInstaller{
Base: NewBase(m, name, buildPipeline),
Base: NewBase(name, buildPipeline),
platform: platform,
repos: filterRepos(repos, name),
kernelName: kernelName,
@ -64,7 +63,6 @@ func NewCoreOSInstaller(m *Manifest,
version: version,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}

View file

@ -20,14 +20,13 @@ type EFIBootTree struct {
KernelOpts []string
}
func NewEFIBootTree(m *Manifest, buildPipeline *Build, product, version string) *EFIBootTree {
func NewEFIBootTree(buildPipeline Build, product, version string) *EFIBootTree {
p := &EFIBootTree{
Base: NewBase(m, "efiboot-tree", buildPipeline),
Base: NewBase("efiboot-tree", buildPipeline),
product: product,
version: version,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}

View file

@ -30,7 +30,7 @@ type ContentTest struct {
// content sources.
func NewContentTest(m *Manifest, name string, packageSets []rpmmd.PackageSet, containers []container.SourceSpec, commits []ostree.SourceSpec) *ContentTest {
pipeline := &ContentTest{
Base: NewBase(m, name, nil),
Base: NewBase(name, nil),
packageSets: packageSets,
containers: containers,
commits: commits,

View file

@ -24,15 +24,14 @@ 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(treePipeline.Manifest(), "bootiso", buildPipeline),
Base: NewBase("bootiso", buildPipeline),
treePipeline: treePipeline,
filename: "image.iso",
isoLabel: isoLabel,
}
buildPipeline.addDependent(p)
treePipeline.Manifest().addPipeline(p)
return p
}

View file

@ -14,13 +14,12 @@ type ISORootfsImg struct {
installerPipeline Pipeline
}
func NewISORootfsImg(buildPipeline *Build, installerPipeline Pipeline) *ISORootfsImg {
func NewISORootfsImg(buildPipeline Build, installerPipeline Pipeline) *ISORootfsImg {
p := &ISORootfsImg{
Base: NewBase(installerPipeline.Manifest(), "rootfs-image", buildPipeline),
Base: NewBase("rootfs-image", buildPipeline),
installerPipeline: installerPipeline,
}
buildPipeline.addDependent(p)
installerPipeline.Manifest().addPipeline(p)
return p
}
@ -50,7 +49,7 @@ func (p *ISORootfsImg) serialize() osbuild.Pipeline {
)
devName := "device"
devices := osbuild.Devices{devName: *lodevice}
devices := map[string]osbuild.Device{devName: *lodevice}
mkfsStage := osbuild.NewMkfsExt4Stage(mkfsStageOptions, devices)
pipeline.AddStage(mkfsStage)
@ -64,8 +63,8 @@ func (p *ISORootfsImg) serialize() osbuild.Pipeline {
},
}
copyStageInputs := osbuild.NewPipelineTreeInputs(inputName, p.installerPipeline.Name())
copyStageMounts := &osbuild.Mounts{*osbuild.NewExt4Mount(devName, devName, "/")}
copyStage := osbuild.NewCopyStage(copyStageOptions, copyStageInputs, &devices, copyStageMounts)
copyStageMounts := []osbuild.Mount{*osbuild.NewExt4Mount(devName, devName, "/")}
copyStage := osbuild.NewCopyStage(copyStageOptions, copyStageInputs, devices, copyStageMounts)
pipeline.AddStage(copyStage)
return pipeline
}

View file

@ -86,7 +86,15 @@ func (m *Manifest) addPipeline(p Pipeline) {
panic("duplicate pipeline name in manifest")
}
}
if p.Manifest() != nil {
panic("pipeline already added to a different manifest")
}
m.pipelines = append(m.pipelines, p)
p.setManifest(m)
// check that the pipeline's build pipeline is included in the same manifest
if build := p.BuildPipeline(); build != nil && build.Manifest() != m {
panic("cannot add pipeline to a different manifest than its build pipeline")
}
}
type PackageSelector func([]rpmmd.PackageSet) []rpmmd.PackageSet

View file

@ -24,14 +24,13 @@ 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(treePipeline.Manifest(), "container", buildPipeline),
Base: NewBase("container", buildPipeline),
treePipeline: treePipeline,
filename: "oci-archive.tar",
}
buildPipeline.addDependent(p)
treePipeline.Manifest().addPipeline(p)
return p
}

View file

@ -125,6 +125,7 @@ type OSCustomizations struct {
LeapSecTZ *string
FactAPIType *facts.APIType
Presets []osbuild.Preset
ContainersStorage *string
Subscription *subscription.ImageOptions
RHSMConfig map[subscription.RHSMStatus]*osbuild.RHSMStageOptions
@ -179,19 +180,15 @@ 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(m *Manifest,
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(m, name, buildPipeline),
Base: NewBase(name, buildPipeline),
repos: filterRepos(repos, name),
platform: platform,
InstallWeakDeps: true,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}
@ -290,7 +287,7 @@ func (p *OS) getBuildPackages(distro Distro) []string {
}
if len(p.OSCustomizations.Containers) > 0 {
if p.OSTreeRef != "" {
if p.OSCustomizations.ContainersStorage != nil {
switch distro {
case DISTRO_EL8:
packages = append(packages, "python3-pytoml")
@ -406,12 +403,8 @@ func (p *OS) serialize() osbuild.Pipeline {
var storagePath string
// OSTree commits do not include data in `/var` since that is tied to the
// deployment, rather than the commit. Therefore the containers need to be
// stored in a different location, like `/usr/share`, and the container
// storage engine configured accordingly.
if p.OSTreeRef != "" {
storagePath = "/usr/share/containers/storage"
if containerStore := p.OSCustomizations.ContainersStorage; containerStore != nil {
storagePath = *containerStore
storageConf := "/etc/containers/storage.conf"
containerStoreOpts := osbuild.NewContainerStorageOptions(storageConf, storagePath)
@ -648,7 +641,7 @@ func (p *OS) serialize() osbuild.Pipeline {
),
)
} else {
options := osbuild.NewGrub2StageOptionsUnified(pt,
options := osbuild.NewGrub2StageOptions(pt,
strings.Join(kernelOptions, " "),
p.kernelVer,
p.platform.GetUEFIVendor() != "",

View file

@ -71,45 +71,44 @@ 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
// commit.
func NewOSTreeCommitDeployment(buildPipeline *Build,
m *Manifest,
func NewOSTreeCommitDeployment(buildPipeline Build,
commit *ostree.SourceSpec,
osName string,
platform platform.Platform) *OSTreeDeployment {
p := &OSTreeDeployment{
Base: NewBase(m, "ostree-deployment", buildPipeline),
Base: NewBase("ostree-deployment", buildPipeline),
commitSource: commit,
osName: osName,
platform: platform,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}
// NewOSTreeDeployment creates a pipeline for an ostree deployment from a
// container
func NewOSTreeContainerDeployment(buildPipeline *Build,
m *Manifest,
func NewOSTreeContainerDeployment(buildPipeline Build,
container *container.SourceSpec,
ref string,
osName string,
platform platform.Platform) *OSTreeDeployment {
p := &OSTreeDeployment{
Base: NewBase(m, "ostree-deployment", buildPipeline),
Base: NewBase("ostree-deployment", buildPipeline),
containerSource: container,
osName: osName,
ref: ref,
platform: platform,
}
buildPipeline.addDependent(p)
m.addPipeline(p)
return p
}
@ -163,7 +162,7 @@ func (p *OSTreeDeployment) serializeStart(packages []rpmmd.PackageSpec, containe
case len(containers) == 1:
p.containerSpec = &containers[0]
default:
panic(fmt.Sprintf("pipeline requires exactly one ostree commit or one container (have commits: %v; containers: %v)", commits, containers))
panic(fmt.Sprintf("pipeline %s requires exactly one ostree commit or one container (have commits: %v; containers: %v)", p.Name(), commits, containers))
}
}
@ -232,12 +231,19 @@ func (p *OSTreeDeployment) doOSTreeContainerSpec(pipeline *osbuild.Pipeline, rep
cont := *p.containerSpec
ref := p.ref
var targetImgref string
// The ostree-remote case is unusual; it may be used by FCOS/Silverblue for example to handle
// embedded GPG signatures
if p.Remote.Name != "" {
targetImgref = fmt.Sprintf("ostree-remote-registry:%s:%s", p.Remote.Name, p.containerSpec.LocalName)
} else {
targetImgref = fmt.Sprintf("ostree-unverified-registry:%s", p.containerSpec.LocalName)
}
options := &osbuild.OSTreeDeployContainerStageOptions{
OsName: p.osName,
KernelOpts: p.KernelOptionsAppend,
// NOTE: setting the target imgref to be the container source but
// we should make this configurable
TargetImgref: fmt.Sprintf("ostree-remote-registry:%s:%s", p.Remote.Name, p.containerSpec.Source),
OsName: p.osName,
KernelOpts: p.KernelOptionsAppend,
TargetImgref: targetImgref,
Mounts: []string{"/boot", "/boot/efi"},
Rootfs: &osbuild.Rootfs{
Label: "root",
@ -408,22 +414,24 @@ func (p *OSTreeDeployment) serialize() osbuild.Pipeline {
}
}
grubOptions := osbuild.NewGrub2StageOptionsUnified(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"},
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)
}
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 {

View file

@ -12,14 +12,13 @@ type OSTreeEncapsulate struct {
inputPipeline Pipeline
}
func NewOSTreeEncapsulate(buildPipeline *Build, inputPipeline Pipeline, pipelinename string) *OSTreeEncapsulate {
func NewOSTreeEncapsulate(buildPipeline Build, inputPipeline Pipeline, pipelinename string) *OSTreeEncapsulate {
p := &OSTreeEncapsulate{
Base: NewBase(inputPipeline.Manifest(), pipelinename, buildPipeline),
Base: NewBase(pipelinename, buildPipeline),
inputPipeline: inputPipeline,
filename: "bootable-container.tar",
}
buildPipeline.addDependent(p)
inputPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -14,13 +14,12 @@ type OVF struct {
}
// NewOVF creates a new OVF pipeline. imgPipeline is the pipeline producing the vmdk image.
func NewOVF(buidPipeline *Build, imgPipeline *VMDK) *OVF {
func NewOVF(buidPipeline Build, imgPipeline *VMDK) *OVF {
p := &OVF{
Base: NewBase(imgPipeline.Manifest(), "ovf", buidPipeline),
Base: NewBase("ovf", buidPipeline),
imgPipeline: imgPipeline,
}
buidPipeline.addDependent(p)
imgPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -25,11 +25,13 @@ type Pipeline interface {
// BuildPipeline returns a reference to the pipeline that creates the build
// root for this pipeline. For build pipelines, it should return nil.
BuildPipeline() *Build
BuildPipeline() Build
// Manifest returns a reference to the Manifest which this Pipeline belongs to.
Manifest() *Manifest
setManifest(*Manifest)
getCheckpoint() bool
getExport() bool
@ -74,7 +76,7 @@ type Pipeline interface {
type Base struct {
manifest *Manifest
name string
build *Build
build Build
checkpoint bool
export bool
}
@ -102,7 +104,7 @@ func (p Base) getExport() bool {
return p.export
}
func (p Base) BuildPipeline() *Build {
func (p Base) BuildPipeline() Build {
return p.build
}
@ -110,6 +112,10 @@ func (p Base) Manifest() *Manifest {
return p.manifest
}
func (p *Base) setManifest(m *Manifest) {
p.manifest = m
}
func (p Base) getBuildPackages(Distro) []string {
return []string{}
}
@ -150,16 +156,10 @@ func (p Base) getInline() []string {
// the build host's filesystem is used as the build root. The runner specifies how to use this
// pipeline as a build pipeline, by naming the distro it contains. When the host system is used
// as a build root, then the necessary runner is autodetected.
func NewBase(m *Manifest, name string, build *Build) Base {
func NewBase(name string, build Build) Base {
p := Base{
manifest: m,
name: name,
build: build,
}
if build != nil {
if build.Base.manifest != m {
panic("build pipeline from a different manifest")
}
name: name,
build: build,
}
return p
}
@ -191,7 +191,7 @@ func (p Base) serialize() osbuild.Pipeline {
type TreePipeline interface {
Name() string
Manifest() *Manifest
BuildPipeline() *Build
BuildPipeline() Build
Platform() platform.Platform
}

View file

@ -25,14 +25,18 @@ func (p *QCOW2) SetFilename(filename string) {
// NewQCOW2 createsa new QCOW2 pipeline. imgPipeline is the pipeline producing the
// raw image. The pipeline name is the name of the new pipeline. Filename is the name
// of the produced qcow2 image.
func NewQCOW2(buildPipeline *Build, imgPipeline FilePipeline) *QCOW2 {
func NewQCOW2(buildPipeline Build, imgPipeline FilePipeline) *QCOW2 {
p := &QCOW2{
Base: NewBase(imgPipeline.Manifest(), "qcow2", buildPipeline),
Base: NewBase("qcow2", buildPipeline),
imgPipeline: imgPipeline,
filename: "image.qcow2",
}
buildPipeline.addDependent(p)
imgPipeline.Manifest().addPipeline(p)
// qcow2 can run outside the build pipeline for e.g. "bib"
if buildPipeline != nil {
buildPipeline.addDependent(p)
} else {
imgPipeline.Manifest().addPipeline(p)
}
return p
}

View file

@ -23,15 +23,14 @@ func (p *RawImage) SetFilename(filename string) {
p.filename = filename
}
func NewRawImage(buildPipeline *Build, treePipeline *OS) *RawImage {
func NewRawImage(buildPipeline Build, treePipeline *OS) *RawImage {
p := &RawImage{
Base: NewBase(treePipeline.Manifest(), "image", buildPipeline),
Base: NewBase("image", buildPipeline),
treePipeline: treePipeline,
filename: "disk.img",
}
buildPipeline.addDependent(p)
p.PartTool = osbuild.PTSfdisk // default; can be changed after initialisation
treePipeline.Manifest().addPipeline(p)
return p
}

View file

@ -25,15 +25,14 @@ func (p *RawOSTreeImage) SetFilename(filename string) {
p.filename = filename
}
func NewRawOStreeImage(buildPipeline *Build, treePipeline *OSTreeDeployment, platform platform.Platform) *RawOSTreeImage {
func NewRawOStreeImage(buildPipeline Build, treePipeline *OSTreeDeployment, platform platform.Platform) *RawOSTreeImage {
p := &RawOSTreeImage{
Base: NewBase(treePipeline.Manifest(), "image", buildPipeline),
Base: NewBase("image", buildPipeline),
treePipeline: treePipeline,
filename: "disk.img",
platform: platform,
}
buildPipeline.addDependent(p)
treePipeline.Manifest().addPipeline(p)
return p
}
@ -86,7 +85,7 @@ func (p *RawOSTreeImage) serialize() osbuild.Pipeline {
// Find the FS root mount name to use as the destination root
// for the target when copying the boot files.
var fsRootMntName string
for _, mnt := range *bootCopyMounts {
for _, mnt := range bootCopyMounts {
if mnt.Target == "/" {
fsRootMntName = mnt.Name
break
@ -111,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)

View file

@ -30,14 +30,13 @@ func (p *Tar) SetFilename(filename string) {
// NewTar creates a new TarPipeline. The inputPipeline represents the
// filesystem tree which will be the contents of the tar file. The pipelinename
// is the name of the pipeline. The filename is the name of the output tar file.
func NewTar(buildPipeline *Build, inputPipeline Pipeline, pipelinename string) *Tar {
func NewTar(buildPipeline Build, inputPipeline Pipeline, pipelinename string) *Tar {
p := &Tar{
Base: NewBase(inputPipeline.Manifest(), pipelinename, buildPipeline),
Base: NewBase(pipelinename, buildPipeline),
inputPipeline: inputPipeline,
filename: "image.tar",
}
buildPipeline.addDependent(p)
inputPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -25,14 +25,13 @@ func (p *VMDK) SetFilename(filename string) {
// raw image. imgOstreePipeline is the pipeline producing the raw ostree image.
// Either imgPipeline or imgOStreePipeline are required, but not both at the same time.
// Filename is the name of the produced image.
func NewVMDK(buildPipeline *Build, imgPipeline FilePipeline) *VMDK {
func NewVMDK(buildPipeline Build, imgPipeline FilePipeline) *VMDK {
p := &VMDK{
Base: NewBase(imgPipeline.Manifest(), "vmdk", buildPipeline),
Base: NewBase("vmdk", buildPipeline),
imgPipeline: imgPipeline,
filename: "image.vmdk",
}
buildPipeline.addDependent(p)
imgPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -26,14 +26,13 @@ func (p *VPC) SetFilename(filename string) {
// NewVPC createsa new Qemu pipeline. imgPipeline is the pipeline producing the
// raw image. The pipeline name is the name of the new pipeline. Filename is the name
// of the produced image.
func NewVPC(buildPipeline *Build, imgPipeline *RawImage) *VPC {
func NewVPC(buildPipeline Build, imgPipeline *RawImage) *VPC {
p := &VPC{
Base: NewBase(imgPipeline.Manifest(), "vpc", buildPipeline),
Base: NewBase("vpc", buildPipeline),
imgPipeline: imgPipeline,
filename: "image.vhd",
}
buildPipeline.addDependent(p)
imgPipeline.Manifest().addPipeline(p)
return p
}

View file

@ -23,14 +23,13 @@ func (p *XZ) SetFilename(filename string) {
// NewXZ creates a new XZ pipeline. imgPipeline is the pipeline producing the
// raw image that will be xz compressed.
func NewXZ(buildPipeline *Build, imgPipeline FilePipeline) *XZ {
func NewXZ(buildPipeline Build, imgPipeline FilePipeline) *XZ {
p := &XZ{
Base: NewBase(imgPipeline.Manifest(), "xz", buildPipeline),
Base: NewBase("xz", buildPipeline),
filename: "image.xz",
imgPipeline: imgPipeline,
}
buildPipeline.addDependent(p)
imgPipeline.Manifest().addPipeline(p)
return p
}