diff --git a/internal/distro/fedora/pipelines.go b/internal/distro/fedora/pipelines.go index b1730b64d..6ad92ae4c 100644 --- a/internal/distro/fedora/pipelines.go +++ b/internal/distro/fedora/pipelines.go @@ -1,23 +1,20 @@ package fedora import ( - "fmt" "math/rand" "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/disk" "github.com/osbuild/osbuild-composer/internal/distro" - pipeline "github.com/osbuild/osbuild-composer/internal/distro/pipelines" osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/pipeline" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) func qcow2Pipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) @@ -32,8 +29,7 @@ func qcow2Pipelines(t *imageType, customizations *blueprint.Customizations, opti pipelines = append(pipelines, treePipeline.Serialize()) diskfile := "disk.img" - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name) - imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch, kernelVer) + imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch) pipelines = append(pipelines, imagePipeline.Serialize()) qemuPipeline := qemuPipeline(&buildPipeline, &imagePipeline, diskfile, t.filename, osbuild.QEMUFormatQCOW2, osbuild.QCOW2Options{Compat: "1.1"}) @@ -45,9 +41,7 @@ func qcow2Pipelines(t *imageType, customizations *blueprint.Customizations, opti func vhdPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) @@ -62,8 +56,7 @@ func vhdPipelines(t *imageType, customizations *blueprint.Customizations, option pipelines = append(pipelines, treePipeline.Serialize()) diskfile := "disk.img" - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name) - imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch, kernelVer) + imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch) pipelines = append(pipelines, imagePipeline.Serialize()) qemuPipeline := qemuPipeline(&buildPipeline, &imagePipeline, diskfile, t.filename, osbuild.QEMUFormatVPC, nil) @@ -74,9 +67,7 @@ func vhdPipelines(t *imageType, customizations *blueprint.Customizations, option func vmdkPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) @@ -91,8 +82,7 @@ func vmdkPipelines(t *imageType, customizations *blueprint.Customizations, optio pipelines = append(pipelines, treePipeline.Serialize()) diskfile := "disk.img" - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name) - imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch, kernelVer) + imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch) pipelines = append(pipelines, imagePipeline.Serialize()) qemuPipeline := qemuPipeline(&buildPipeline, &imagePipeline, diskfile, t.filename, osbuild.QEMUFormatVMDK, osbuild.VMDKOptions{Subformat: osbuild.VMDKSubformatStreamOptimized}) @@ -103,9 +93,7 @@ func vmdkPipelines(t *imageType, customizations *blueprint.Customizations, optio func openstackPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) @@ -120,8 +108,7 @@ func openstackPipelines(t *imageType, customizations *blueprint.Customizations, pipelines = append(pipelines, treePipeline.Serialize()) diskfile := "disk.img" - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name) - imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch, kernelVer) + imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch) pipelines = append(pipelines, imagePipeline.Serialize()) qemuPipeline := qemuPipeline(&buildPipeline, &imagePipeline, diskfile, t.filename, osbuild.QEMUFormatQCOW2, nil) @@ -134,9 +121,7 @@ func ec2CommonPipelines(t *imageType, customizations *blueprint.Customizations, rng *rand.Rand, diskfile string) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) @@ -150,8 +135,7 @@ func ec2CommonPipelines(t *imageType, customizations *blueprint.Customizations, } pipelines = append(pipelines, treePipeline.Serialize()) - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name) - imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch, kernelVer) + imagePipeline := liveImagePipeline(&buildPipeline, &treePipeline, diskfile, partitionTable, t.arch) pipelines = append(pipelines, imagePipeline.Serialize()) return pipelines, nil } @@ -164,29 +148,22 @@ func ec2Pipelines(t *imageType, customizations *blueprint.Customizations, option func iotInstallerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) pipelines = append(pipelines, buildPipeline.Serialize()) installerPackages := packageSetSpecs[installerPkgsKey] d := t.arch.distro - archName := t.Arch().Name() - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(installerPackages, "kernel") ksUsers := len(customizations.GetUsers())+len(customizations.GetGroups()) > 0 - isolabel := fmt.Sprintf(d.isolabelTmpl, archName) - anacondaTreePipeline := anacondaTreePipeline(&buildPipeline, repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "IoT", ksUsers) - isoTreePipeline := bootISOTreePipeline(&buildPipeline, &anacondaTreePipeline.Pipeline, options, kernelVer, archName, d.vendor, d.product, d.osVersion, isolabel, customizations.GetUsers(), customizations.GetGroups()) - isoPipeline := bootISOPipeline(&buildPipeline, &isoTreePipeline, t.Filename(), isolabel, false) + anacondaTreePipeline := anacondaTreePipeline(&buildPipeline, repos, installerPackages, t.Arch().Name(), d.product, d.osVersion, "IoT", ksUsers) + isoTreePipeline := bootISOTreePipeline(&buildPipeline, &anacondaTreePipeline, options, d.vendor, d.isolabelTmpl, customizations.GetUsers(), customizations.GetGroups()) + isoPipeline := bootISOPipeline(&buildPipeline, &isoTreePipeline, t.Filename(), false) return append(pipelines, anacondaTreePipeline.Serialize(), isoTreePipeline.Serialize(), isoPipeline.Serialize()), nil } func iotCorePipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec) (*pipeline.BuildPipeline, *pipeline.OSPipeline, *pipeline.OSTreeCommitPipeline, error) { - buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner) - buildPipeline.Repos = repos - buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey] + buildPipeline := pipeline.NewBuildPipeline(t.arch.distro.runner, repos, packageSetSpecs[buildPkgsKey]) treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, nil) if err != nil { return nil, nil, nil, err @@ -236,7 +213,7 @@ func osPipeline(buildPipeline *pipeline.BuildPipeline, imageConfig := t.getDefaultImageConfig() - pl := pipeline.NewOSPipeline(buildPipeline, t.rpmOstree) + pl := pipeline.NewOSPipeline(buildPipeline, t.rpmOstree, repos, packages, c.GetKernel().Name) pl.PartitionTable = pt @@ -258,13 +235,9 @@ func osPipeline(buildPipeline *pipeline.BuildPipeline, kernelOptions = append(kernelOptions, bpKernel.Append) } pl.KernelOptionsAppend = kernelOptions - pl.KernelName = c.GetKernel().Name pl.OSTreeParent = options.OSTree.Parent pl.OSTreeURL = options.OSTree.URL - - pl.Repos = repos - pl.PackageSpecs = packages pl.GPGKeyFiles = imageConfig.GPGKeyFiles if !t.bootISO { @@ -340,8 +313,7 @@ func osPipeline(buildPipeline *pipeline.BuildPipeline, } func ostreeCommitPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.OSPipeline, options distro.ImageOptions, osVersion string) pipeline.OSTreeCommitPipeline { - p := pipeline.NewOSTreeCommitPipeline(buildPipeline, treePipeline) - p.Ref = options.OSTree.Ref + p := pipeline.NewOSTreeCommitPipeline(buildPipeline, treePipeline, options.OSTree.Ref) p.OSVersion = osVersion p.Parent = options.OSTree.Parent return p @@ -369,30 +341,19 @@ func containerPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipe return p } -func anacondaTreePipeline(buildPipeline *pipeline.BuildPipeline, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string, users bool) pipeline.AnacondaPipeline { - p := pipeline.NewAnacondaPipeline(buildPipeline) - p.Repos = repos - p.PackageSpecs = packages +func anacondaTreePipeline(buildPipeline *pipeline.BuildPipeline, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, arch, product, osVersion, variant string, users bool) pipeline.AnacondaPipeline { + p := pipeline.NewAnacondaPipeline(buildPipeline, repos, packages, "kernel", arch, product, osVersion) p.Users = users - p.KernelVer = kernelVer - p.Arch = arch - p.Product = product - p.OSVersion = osVersion p.Variant = variant - p.Biosdevname = (p.Arch == distro.X86_64ArchName) + p.Biosdevname = (arch == distro.X86_64ArchName) return p } -func bootISOTreePipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.Pipeline, options distro.ImageOptions, kernelVer, arch, vendor, product, osVersion, isolabel string, users []blueprint.UserCustomization, groups []blueprint.GroupCustomization) pipeline.ISOTreePipeline { - p := pipeline.NewISOTreePipeline(buildPipeline, treePipeline) - p.KernelVer = kernelVer - p.Arch = arch +func bootISOTreePipeline(buildPipeline *pipeline.BuildPipeline, anacondaPipeline *pipeline.AnacondaPipeline, options distro.ImageOptions, vendor, isoLabelTempl string, users []blueprint.UserCustomization, groups []blueprint.GroupCustomization) pipeline.ISOTreePipeline { + p := pipeline.NewISOTreePipeline(buildPipeline, anacondaPipeline, isoLabelTempl) p.Release = "202010217.n.0" - p.Vendor = vendor - p.Product = product p.OSName = "fedora" - p.OSVersion = osVersion - p.ISOLabel = isolabel + p.Vendor = vendor p.Users = users p.Groups = groups p.OSTreeRef = options.OSTree.Ref @@ -401,15 +362,14 @@ func bootISOTreePipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pi return p } -func bootISOPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.ISOTreePipeline, filename, isolabel string, isolinux bool) pipeline.ISOPipeline { +func bootISOPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.ISOTreePipeline, filename string, isolinux bool) pipeline.ISOPipeline { p := pipeline.NewISOPipeline(buildPipeline, treePipeline) p.Filename = filename - p.ISOLabel = isolabel p.ISOLinux = isolinux return p } -func liveImagePipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.OSPipeline, outputFilename string, pt *disk.PartitionTable, arch *architecture, kernelVer string) pipeline.LiveImgPipeline { +func liveImagePipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.OSPipeline, outputFilename string, pt *disk.PartitionTable, arch *architecture) pipeline.LiveImgPipeline { p := pipeline.NewLiveImgPipeline(buildPipeline, treePipeline) p.Filename = outputFilename @@ -421,7 +381,6 @@ func liveImagePipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipe p.GRUBLegacy = arch.legacy } - p.KernelVer = kernelVer p.PartitionTable = *pt return p diff --git a/internal/distro/pipelines/anaconda.go b/internal/distro/pipelines/anaconda.go index 067611b19..7327f9e50 100644 --- a/internal/distro/pipelines/anaconda.go +++ b/internal/distro/pipelines/anaconda.go @@ -5,34 +5,87 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) +// An AnacondaPipeline represents the installer tree as found on an ISO. type AnacondaPipeline struct { Pipeline - Repos []rpmmd.RepoConfig - PackageSpecs []rpmmd.PackageSpec - Users bool - KernelVer string - Arch string - Product string - OSVersion string - Variant string - Biosdevname bool + // Users indicate whether or not the user spoke should be enabled in + // anaconda. If it is, users specified in a kickstart will be configured, + // and in case no users are provided in a kickstart the user will be + // prompted to configure them at install time. If this is set to false + // any kickstart provided users are ignored and the user is never + // prompted to configure users during installation. + Users bool + // Biosdevname indicates whether or not biosdevname should be used to + // name network devices when booting the installer. This may affect + // the naming of network devices on the target system. + Biosdevname bool + // Variant is the variant of the product being installed. + // TODO: what should be the default value? + Variant string + + repos []rpmmd.RepoConfig + packageSpecs []rpmmd.PackageSpec + kernelVer string + arch string + product string + version string } -func NewAnacondaPipeline(buildPipeline *BuildPipeline) AnacondaPipeline { +// NewAnacondaPipeline creates an anaconda pipeline object. repos and packages +// indicate the content to build the installer from, which is distinct from the +// packages the installer will install on the target system. kernelName is the +// name of the kernel package the intsaller will use. arch is the supported +// architecture. Product and version refers to the product the installer is the +// installer for. +func NewAnacondaPipeline(buildPipeline *BuildPipeline, + repos []rpmmd.RepoConfig, + packages []rpmmd.PackageSpec, + kernelName, + arch, + product, + version string) AnacondaPipeline { + kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packages, kernelName) return AnacondaPipeline{ - Pipeline: New("anaconda-tree", &buildPipeline.Pipeline), + Pipeline: New("anaconda-tree", buildPipeline, nil), + repos: repos, + packageSpecs: packages, + kernelVer: kernelVer, + arch: arch, + product: product, + version: version, } } +// KernelVer returns the NEVRA of the kernel package the installer will use at +// install time. +func (p AnacondaPipeline) KernelVer() string { + return p.kernelVer +} + +// Arch returns the supported architecture. +func (p AnacondaPipeline) Arch() string { + return p.arch +} + +// Product returns the product being installed. +func (p AnacondaPipeline) Product() string { + return p.product +} + +// Version returns the version of the product being installed. +func (p AnacondaPipeline) Version() string { + return p.version +} + func (p AnacondaPipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() - pipeline.AddStage(osbuild2.NewRPMStage(osbuild2.NewRPMStageOptions(p.Repos), osbuild2.NewRpmStageSourceFilesInputs(p.PackageSpecs))) + pipeline.AddStage(osbuild2.NewRPMStage(osbuild2.NewRPMStageOptions(p.repos), osbuild2.NewRpmStageSourceFilesInputs(p.packageSpecs))) pipeline.AddStage(osbuild2.NewBuildstampStage(&osbuild2.BuildstampStageOptions{ - Arch: p.Arch, - Product: p.Product, - Version: p.OSVersion, + Arch: p.Arch(), + Product: p.Product(), Variant: p.Variant, + Version: p.Version(), Final: true, })) pipeline.AddStage(osbuild2.NewLocaleStage(&osbuild2.LocaleStageOptions{Language: "en_US.UTF-8"})) @@ -65,9 +118,9 @@ func (p AnacondaPipeline) Serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewAnacondaStage(osbuild2.NewAnacondaStageOptions(p.Users))) pipeline.AddStage(osbuild2.NewLoraxScriptStage(&osbuild2.LoraxScriptStageOptions{ Path: "99-generic/runtime-postinstall.tmpl", - BaseArch: p.Arch, + BaseArch: p.Arch(), })) - pipeline.AddStage(osbuild2.NewDracutStage(dracutStageOptions(p.KernelVer, p.Biosdevname, []string{ + pipeline.AddStage(osbuild2.NewDracutStage(dracutStageOptions(p.KernelVer(), p.Biosdevname, []string{ "anaconda", "rdma", "rngd", diff --git a/internal/distro/pipelines/build.go b/internal/distro/pipelines/build.go index 20e0fd3b1..24b649846 100644 --- a/internal/distro/pipelines/build.go +++ b/internal/distro/pipelines/build.go @@ -7,22 +7,24 @@ import ( type BuildPipeline struct { Pipeline - Repos []rpmmd.RepoConfig - PackageSpecs []rpmmd.PackageSpec + + repos []rpmmd.RepoConfig + packageSpecs []rpmmd.PackageSpec } -func NewBuildPipeline(runner string) BuildPipeline { +func NewBuildPipeline(runner string, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec) BuildPipeline { pipeline := BuildPipeline{ - Pipeline: New("build", nil), + Pipeline: New("build", nil, &runner), + repos: repos, + packageSpecs: packages, } - pipeline.runner = &runner return pipeline } func (p BuildPipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() - pipeline.AddStage(osbuild2.NewRPMStage(osbuild2.NewRPMStageOptions(p.Repos), osbuild2.NewRpmStageSourceFilesInputs(p.PackageSpecs))) + pipeline.AddStage(osbuild2.NewRPMStage(osbuild2.NewRPMStageOptions(p.repos), osbuild2.NewRpmStageSourceFilesInputs(p.packageSpecs))) pipeline.AddStage(osbuild2.NewSELinuxStage(selinuxStageOptions(true))) return pipeline diff --git a/internal/distro/pipelines/commit.go b/internal/distro/pipelines/commit.go index 8a6fed2cd..c0f959443 100644 --- a/internal/distro/pipelines/commit.go +++ b/internal/distro/pipelines/commit.go @@ -7,18 +7,24 @@ import ( type OSTreeCommitPipeline struct { Pipeline treePipeline *OSPipeline - Ref string OSVersion string Parent string + + ref string } -func NewOSTreeCommitPipeline(buildPipeline *BuildPipeline, treePipeline *OSPipeline) OSTreeCommitPipeline { +func NewOSTreeCommitPipeline(buildPipeline *BuildPipeline, treePipeline *OSPipeline, ref string) OSTreeCommitPipeline { return OSTreeCommitPipeline{ - Pipeline: New("ostree-commit", &buildPipeline.Pipeline), + Pipeline: New("ostree-commit", buildPipeline, nil), treePipeline: treePipeline, + ref: ref, } } +func (p OSTreeCommitPipeline) Ref() string { + return p.ref +} + func (p OSTreeCommitPipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() @@ -31,7 +37,7 @@ func (p OSTreeCommitPipeline) Serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewOSTreeCommitStage( &osbuild2.OSTreeCommitStageOptions{ - Ref: p.Ref, + Ref: p.Ref(), OSVersion: p.OSVersion, Parent: p.Parent, }, diff --git a/internal/distro/pipelines/commit_server_tree.go b/internal/distro/pipelines/commit_server_tree.go index b03e54fd1..6c17dfada 100644 --- a/internal/distro/pipelines/commit_server_tree.go +++ b/internal/distro/pipelines/commit_server_tree.go @@ -20,7 +20,7 @@ type OSTreeCommitServerTreePipeline struct { func NewOSTreeCommitServerTreePipeline(buildPipeline *BuildPipeline, commitPipeline *OSTreeCommitPipeline) OSTreeCommitServerTreePipeline { return OSTreeCommitServerTreePipeline{ - Pipeline: New("container-tree", &buildPipeline.Pipeline), + Pipeline: New("container-tree", buildPipeline, nil), commitPipeline: commitPipeline, Language: "en_US", } @@ -38,7 +38,7 @@ func (p OSTreeCommitServerTreePipeline) Serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewOSTreePullStage( &osbuild2.OSTreePullStageOptions{Repo: repoPath}, - osbuild2.NewOstreePullStageInputs("org.osbuild.pipeline", "name:"+p.commitPipeline.Name(), p.commitPipeline.Ref), + osbuild2.NewOstreePullStageInputs("org.osbuild.pipeline", "name:"+p.commitPipeline.Name(), p.commitPipeline.Ref()), )) // make nginx log and lib directories world writeable, otherwise nginx can't start in diff --git a/internal/distro/pipelines/iso.go b/internal/distro/pipelines/iso.go index bad613133..4934a61f4 100644 --- a/internal/distro/pipelines/iso.go +++ b/internal/distro/pipelines/iso.go @@ -8,13 +8,12 @@ type ISOPipeline struct { Pipeline treePipeline *ISOTreePipeline Filename string - ISOLabel string ISOLinux bool } func NewISOPipeline(buildPipeline *BuildPipeline, treePipeline *ISOTreePipeline) ISOPipeline { return ISOPipeline{ - Pipeline: New("bootiso", &buildPipeline.Pipeline), + Pipeline: New("bootiso", buildPipeline, nil), treePipeline: treePipeline, } } @@ -22,7 +21,7 @@ func NewISOPipeline(buildPipeline *BuildPipeline, treePipeline *ISOTreePipeline) func (p ISOPipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() - pipeline.AddStage(osbuild2.NewXorrisofsStage(xorrisofsStageOptions(p.Filename, p.ISOLabel, p.ISOLinux), osbuild2.NewXorrisofsStagePipelineTreeInputs(p.treePipeline.Name()))) + pipeline.AddStage(osbuild2.NewXorrisofsStage(xorrisofsStageOptions(p.Filename, p.treePipeline.ISOLabel(), p.ISOLinux), osbuild2.NewXorrisofsStagePipelineTreeInputs(p.treePipeline.Name()))) pipeline.AddStage(osbuild2.NewImplantisomd5Stage(&osbuild2.Implantisomd5StageOptions{Filename: p.Filename})) return pipeline diff --git a/internal/distro/pipelines/iso_tree.go b/internal/distro/pipelines/iso_tree.go index 0282e6377..0de4dc829 100644 --- a/internal/distro/pipelines/iso_tree.go +++ b/internal/distro/pipelines/iso_tree.go @@ -11,35 +11,40 @@ import ( type ISOTreePipeline struct { Pipeline - treePipeline *Pipeline - KernelVer string - Arch string - Release string - Vendor string - Product string - OSName string - OSVersion string - ISOLabel string - Users []blueprint.UserCustomization - Groups []blueprint.GroupCustomization - OSTreeParent string - OSTreeRef string + anacondaPipeline *AnacondaPipeline + Vendor string + OSName string + Release string + Users []blueprint.UserCustomization + Groups []blueprint.GroupCustomization + OSTreeParent string + OSTreeRef string + + isoLabel string } -func NewISOTreePipeline(buildPipeline *BuildPipeline, treePipeline *Pipeline) ISOTreePipeline { +func NewISOTreePipeline(buildPipeline *BuildPipeline, anacondaPipeline *AnacondaPipeline, isoLabelTmpl string) ISOTreePipeline { + // TODO: replace isoLabelTmpl with more high-level properties + isoLabel := fmt.Sprintf(isoLabelTmpl, anacondaPipeline.Arch()) + return ISOTreePipeline{ - Pipeline: New("bootiso-tree", &buildPipeline.Pipeline), - treePipeline: treePipeline, + Pipeline: New("bootiso-tree", buildPipeline, nil), + anacondaPipeline: anacondaPipeline, + isoLabel: isoLabel, } } +func (p ISOTreePipeline) ISOLabel() string { + return p.isoLabel +} + func (p ISOTreePipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() kspath := "/osbuild.ks" ostreeRepoPath := "/ostree/repo" - pipeline.AddStage(osbuild2.NewBootISOMonoStage(bootISOMonoStageOptions(p.KernelVer, p.Arch, p.Vendor, p.Product, p.OSVersion, p.ISOLabel, kspath), osbuild2.NewBootISOMonoStagePipelineTreeInputs(p.treePipeline.Name()))) + pipeline.AddStage(osbuild2.NewBootISOMonoStage(bootISOMonoStageOptions(p.anacondaPipeline.KernelVer(), p.anacondaPipeline.Arch(), p.Vendor, p.anacondaPipeline.Product(), p.anacondaPipeline.Version(), p.ISOLabel(), kspath), osbuild2.NewBootISOMonoStagePipelineTreeInputs(p.anacondaPipeline.Name()))) kickstartOptions, err := osbuild2.NewKickstartStageOptions(kspath, "", p.Users, p.Groups, makeISORootPath(ostreeRepoPath), p.OSTreeRef, p.OSName) if err != nil { @@ -48,7 +53,7 @@ func (p ISOTreePipeline) Serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewKickstartStage(kickstartOptions)) pipeline.AddStage(osbuild2.NewDiscinfoStage(&osbuild2.DiscinfoStageOptions{ - BaseArch: p.Arch, + BaseArch: p.anacondaPipeline.Arch(), Release: p.Release, })) diff --git a/internal/distro/pipelines/live.go b/internal/distro/pipelines/live.go index aa803615d..5f0b39703 100644 --- a/internal/distro/pipelines/live.go +++ b/internal/distro/pipelines/live.go @@ -10,15 +10,14 @@ type LiveImgPipeline struct { PartitionTable disk.PartitionTable BootLoader BootLoader GRUBLegacy string - KernelVer string - treePipeline *Pipeline + treePipeline *OSPipeline Filename string } func NewLiveImgPipeline(buildPipeline *BuildPipeline, treePipeline *OSPipeline) LiveImgPipeline { return LiveImgPipeline{ - Pipeline: New("image", &buildPipeline.Pipeline), - treePipeline: &treePipeline.Pipeline, + Pipeline: New("image", buildPipeline, nil), + treePipeline: treePipeline, } } @@ -45,7 +44,7 @@ func (p LiveImgPipeline) Serialize() osbuild2.Pipeline { } case BOOTLOADER_ZIPL: loopback := osbuild2.NewLoopbackDevice(&osbuild2.LoopbackDeviceOptions{Filename: p.Filename}) - pipeline.AddStage(osbuild2.NewZiplInstStage(osbuild2.NewZiplInstStageOptions(p.KernelVer, &p.PartitionTable), loopback, copyDevices, copyMounts)) + pipeline.AddStage(osbuild2.NewZiplInstStage(osbuild2.NewZiplInstStageOptions(p.treePipeline.KernelVer(), &p.PartitionTable), loopback, copyDevices, copyMounts)) } return pipeline diff --git a/internal/distro/pipelines/oci_container.go b/internal/distro/pipelines/oci_container.go index 08a074aa6..41e606d57 100644 --- a/internal/distro/pipelines/oci_container.go +++ b/internal/distro/pipelines/oci_container.go @@ -15,7 +15,7 @@ type OCIContainerPipeline struct { func NewOCIContainerPipeline(buildPipeline *BuildPipeline, treePipeline *Pipeline) OCIContainerPipeline { return OCIContainerPipeline{ - Pipeline: New("container", &buildPipeline.Pipeline), + Pipeline: New("container", buildPipeline, nil), treePipeline: treePipeline, } } diff --git a/internal/distro/pipelines/os.go b/internal/distro/pipelines/os.go index 7e3bec473..0387ffde8 100644 --- a/internal/distro/pipelines/os.go +++ b/internal/distro/pipelines/os.go @@ -14,17 +14,13 @@ import ( type OSPipeline struct { Pipeline - osTree bool OSTreeParent string OSTreeURL string - KernelName string KernelOptionsAppend []string BootLoader BootLoader UEFI bool GRUBLegacy string Vendor string - Repos []rpmmd.RepoConfig - PackageSpecs []rpmmd.PackageSpec GPGKeyFiles []string PartitionTable *disk.PartitionTable Language string @@ -58,20 +54,33 @@ type OSPipeline struct { AuthConfig *osbuild2.AuthconfigStageOptions PwQuality *osbuild2.PwqualityConfStageOptions WAAgentConfig *osbuild2.WAAgentConfStageOptions + + osTree bool + repos []rpmmd.RepoConfig + packageSpecs []rpmmd.PackageSpec + kernelVer string } -func NewOSPipeline(buildPipeline *BuildPipeline, osTree bool) OSPipeline { +func NewOSPipeline(buildPipeline *BuildPipeline, osTree bool, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelName string) OSPipeline { name := "os" if osTree { name = "ostree-tree" } + kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packages, kernelName) return OSPipeline{ - Pipeline: New(name, &buildPipeline.Pipeline), - osTree: osTree, - Hostname: "localhost.localdomain", + Pipeline: New(name, buildPipeline, nil), + osTree: osTree, + repos: repos, + packageSpecs: packages, + kernelVer: kernelVer, + Hostname: "localhost.localdomain", } } +func (p OSPipeline) KernelVer() string { + return p.kernelVer +} + func (p OSPipeline) Serialize() osbuild2.Pipeline { pipeline := p.Pipeline.Serialize() @@ -79,9 +88,9 @@ func (p OSPipeline) Serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewOSTreePasswdStage("org.osbuild.source", p.OSTreeParent)) } - rpmOptions := osbuild2.NewRPMStageOptions(p.Repos) + rpmOptions := osbuild2.NewRPMStageOptions(p.repos) rpmOptions.GPGKeysFromTree = p.GPGKeyFiles - pipeline.AddStage(osbuild2.NewRPMStage(rpmOptions, osbuild2.NewRpmStageSourceFilesInputs(p.PackageSpecs))) + pipeline.AddStage(osbuild2.NewRPMStage(rpmOptions, osbuild2.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 { @@ -229,8 +238,7 @@ func (p OSPipeline) Serialize() osbuild2.Pipeline { var bootloader *osbuild2.Stage switch p.BootLoader { case BOOTLOADER_GRUB: - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(p.PackageSpecs, p.KernelName) - options := osbuild2.NewGrub2StageOptionsUnified(p.PartitionTable, kernelVer, p.UEFI, p.GRUBLegacy, p.Vendor, false) + options := osbuild2.NewGrub2StageOptionsUnified(p.PartitionTable, p.kernelVer, p.UEFI, p.GRUBLegacy, p.Vendor, 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` diff --git a/internal/distro/pipelines/pipeline.go b/internal/distro/pipelines/pipeline.go index 3a66b8629..347427ddb 100644 --- a/internal/distro/pipelines/pipeline.go +++ b/internal/distro/pipelines/pipeline.go @@ -1,3 +1,9 @@ +// Package pipeline implements a standard set of osbuild pipelines. A pipeline +// conceptually represents a named filesystem tree, optionally generated +// in a provided build root (represented by another pipeline). All inputs +// to a pipeline must be explicitly specified, either in terms of other +// pipeline, in terms of content addressable inputs or in terms of static +// parameters to the inherited Pipeline structs. package pipeline import ( @@ -11,35 +17,55 @@ const ( BOOTLOADER_ZIPL ) +// A Pipeline represents the core functionality shared between each of the pipeline +// implementations, and the Pipeline struct must be embedded in each of them. type Pipeline struct { name string - runner *string - build *Pipeline + runner string + build *BuildPipeline } +// Name returns the name of the pipeline. The name must be unique for a given manifest. +// Pipeline names are used to refer to pipelines either as dependencies between pipelines +// or for exporting them. func (p Pipeline) Name() string { return p.name } -func New(name string, build *Pipeline) Pipeline { - return Pipeline{ +// New returns a generic Pipeline object. The name is mandatory, immutable and must +// be unique among all the pipelines used in a manifest, which is currently not enforced. +// The build argument is a pipeline representing a build root in which the rest of the +// pipeline is built. In order to ensure reproducibility a build pipeline must always be +// provided, except for int he build pipeline itself. When a build pipeline is not provided +// the build host's filesystem is used as the build root, and in this case a runner must be +// specified which knows how to interpret the host filesystem as a build root. +func New(name string, build *BuildPipeline, runner *string) Pipeline { + p := Pipeline{ name: name, build: build, } + if runner != nil { + if build != nil { + panic("both runner and build pipeline specified") + } + p.runner = *runner + } else if build == nil { + panic("neither build pipeline nor runner specified") + } + return p } +// Serialize turns a given pipeline into an osbuild2.Pipeline object. This object is +// meant to be treated as opaque and not to be modified further outside of the pipeline +// package. func (p Pipeline) Serialize() osbuild2.Pipeline { var buildName string if p.build != nil { buildName = "name:" + p.build.Name() } - var runner string - if p.runner != nil { - runner = *p.runner - } return osbuild2.Pipeline{ Name: p.name, - Runner: runner, + Runner: p.runner, Build: buildName, } } diff --git a/internal/distro/pipelines/qemu.go b/internal/distro/pipelines/qemu.go index 164192c2d..49db7ab0e 100644 --- a/internal/distro/pipelines/qemu.go +++ b/internal/distro/pipelines/qemu.go @@ -16,7 +16,7 @@ type QemuPipeline struct { func NewQemuPipeline(buildPipeline *BuildPipeline, imgPipeline *LiveImgPipeline, name string) QemuPipeline { return QemuPipeline{ - Pipeline: New(name, &buildPipeline.Pipeline), + Pipeline: New(name, buildPipeline, nil), imgPipeline: &imgPipeline.Pipeline, } } diff --git a/internal/distro/pipelines/tar.go b/internal/distro/pipelines/tar.go index 327fca6f1..1ac54f38f 100644 --- a/internal/distro/pipelines/tar.go +++ b/internal/distro/pipelines/tar.go @@ -12,7 +12,7 @@ type TarPipeline struct { func NewTarPipeline(buildPipeline *BuildPipeline, inputPipeline *Pipeline, name string) TarPipeline { return TarPipeline{ - Pipeline: New(name, &buildPipeline.Pipeline), + Pipeline: New(name, buildPipeline, nil), inputPipeline: inputPipeline, } }