pipelines: add Fedora IoT installer pipelines

This commit is contained in:
Tom Gundersen 2022-06-26 00:49:51 +01:00
parent 284eb9af59
commit 040a4ef6a1
5 changed files with 334 additions and 253 deletions

View file

@ -3,7 +3,6 @@ package fedora
import (
"fmt"
"math/rand"
"path"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/disk"
@ -162,13 +161,6 @@ func ec2Pipelines(t *imageType, customizations *blueprint.Customizations, option
return ec2CommonPipelines(t, customizations, options, repos, packageSetSpecs, rng, t.Filename())
}
//makeISORootPath return a path that can be used to address files and folders in
//the root of the iso
func makeISORootPath(p string) string {
fullpath := path.Join("/run/install/repo", p)
return fmt.Sprintf("file://%s", fullpath)
}
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)
@ -181,21 +173,14 @@ func iotInstallerPipelines(t *imageType, customizations *blueprint.Customization
d := t.arch.distro
archName := t.Arch().Name()
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(installerPackages, "kernel")
ostreeRepoPath := "/ostree/repo"
payloadStages := ostreePayloadStages(options, ostreeRepoPath)
kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", customizations.GetUsers(), customizations.GetGroups(), makeISORootPath(ostreeRepoPath), options.OSTree.Ref, "fedora")
if err != nil {
return nil, err
}
ksUsers := len(customizations.GetUsers())+len(customizations.GetGroups()) > 0
pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "IoT", ksUsers))
isolabel := fmt.Sprintf(d.isolabelTmpl, archName)
pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, archName, d.vendor, d.product, d.osVersion, isolabel, kickstartOptions, payloadStages))
pipelines = append(pipelines, *bootISOPipeline(t.Filename(), d.isolabelTmpl, archName, false))
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)
return pipelines, nil
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) {
@ -384,93 +369,43 @@ func containerPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipe
return p
}
func ostreePayloadStages(options distro.ImageOptions, ostreeRepoPath string) []*osbuild.Stage {
stages := make([]*osbuild.Stage, 0)
// ostree commit payload
stages = append(stages, osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath}))
stages = append(stages, osbuild.NewOSTreePullStage(
&osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath},
osbuild.NewOstreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref),
))
return stages
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
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)
return p
}
func anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string, users bool) *osbuild.Pipeline {
p := new(osbuild.Pipeline)
p.Name = "anaconda-tree"
p.Build = "name:build"
p.AddStage(osbuild.NewRPMStage(osbuild.NewRPMStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(packages)))
p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch, product, osVersion, variant)))
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"}))
rootPassword := ""
rootUser := osbuild.UsersStageOptionsUser{
Password: &rootPassword,
}
installUID := 0
installGID := 0
installHome := "/root"
installShell := "/usr/libexec/anaconda/run-anaconda"
installPassword := ""
installUser := osbuild.UsersStageOptionsUser{
UID: &installUID,
GID: &installGID,
Home: &installHome,
Shell: &installShell,
Password: &installPassword,
}
usersStageOptions := &osbuild.UsersStageOptions{
Users: map[string]osbuild.UsersStageOptionsUser{
"root": rootUser,
"install": installUser,
},
}
p.AddStage(osbuild.NewUsersStage(usersStageOptions))
p.AddStage(osbuild.NewAnacondaStage(osbuild.NewAnacondaStageOptions(users)))
p.AddStage(osbuild.NewLoraxScriptStage(loraxScriptStageOptions(arch)))
p.AddStage(osbuild.NewDracutStage(dracutStageOptions(kernelVer, arch, []string{
"anaconda",
"rdma",
"rngd",
"multipath",
"fcoe",
"fcoe-uefi",
"iscsi",
"lunmask",
"nfs",
})))
p.AddStage(osbuild.NewSELinuxConfigStage(&osbuild.SELinuxConfigStageOptions{State: osbuild.SELinuxStatePermissive}))
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
p.Release = "202010217.n.0"
p.Vendor = vendor
p.Product = product
p.OSName = "fedora"
p.OSVersion = osVersion
p.ISOLabel = isolabel
p.Users = users
p.Groups = groups
p.OSTreeRef = options.OSTree.Ref
p.OSTreeParent = options.OSTree.Parent
return p
}
func bootISOTreePipeline(kernelVer, arch, vendor, product, osVersion, isolabel string, ksOptions *osbuild.KickstartStageOptions, payloadStages []*osbuild.Stage) *osbuild.Pipeline {
p := new(osbuild.Pipeline)
p.Name = "bootiso-tree"
p.Build = "name:build"
p.AddStage(osbuild.NewBootISOMonoStage(bootISOMonoStageOptions(kernelVer, arch, vendor, product, osVersion, isolabel), osbuild.NewBootISOMonoStagePipelineTreeInputs("anaconda-tree")))
p.AddStage(osbuild.NewKickstartStage(ksOptions))
p.AddStage(osbuild.NewDiscinfoStage(discinfoStageOptions(arch)))
for _, stage := range payloadStages {
p.AddStage(stage)
}
return p
}
func bootISOPipeline(filename, isolabel, arch string, isolinux bool) *osbuild.Pipeline {
p := new(osbuild.Pipeline)
p.Name = "bootiso"
p.Build = "name:build"
p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, isolabel, arch, isolinux), osbuild.NewXorrisofsStagePipelineTreeInputs("bootiso-tree")))
p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename}))
func bootISOPipeline(buildPipeline *pipeline.BuildPipeline, treePipeline *pipeline.ISOTreePipeline, filename, isolabel string, isolinux bool) pipeline.ISOPipeline {
p := pipeline.NewISOPipeline(buildPipeline, treePipeline)
p.Filename = filename
p.ISOLabel = isolabel
p.ISOLinux = isolinux
return p
}

View file

@ -1,153 +0,0 @@
package fedora
import (
"fmt"
"github.com/osbuild/osbuild-composer/internal/distro"
osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2"
)
const (
kspath = "/osbuild.ks"
)
func buildStampStageOptions(arch, product, osVersion, variant string) *osbuild.BuildstampStageOptions {
return &osbuild.BuildstampStageOptions{
Arch: arch,
Product: product,
Version: osVersion,
Variant: variant,
Final: true,
}
}
func loraxScriptStageOptions(arch string) *osbuild.LoraxScriptStageOptions {
return &osbuild.LoraxScriptStageOptions{
Path: "99-generic/runtime-postinstall.tmpl",
BaseArch: arch,
}
}
func dracutStageOptions(kernelVer, arch string, additionalModules []string) *osbuild.DracutStageOptions {
kernel := []string{kernelVer}
modules := []string{
"bash",
"systemd",
"fips",
"systemd-initrd",
"modsign",
"nss-softokn",
"i18n",
"convertfs",
"network-manager",
"network",
"ifcfg",
"url-lib",
"drm",
"plymouth",
"crypt",
"dm",
"dmsquash-live",
"kernel-modules",
"kernel-modules-extra",
"kernel-network-modules",
"livenet",
"lvm",
"mdraid",
"qemu",
"qemu-net",
"resume",
"rootfs-block",
"terminfo",
"udev-rules",
"dracut-systemd",
"pollcdrom",
"usrmount",
"base",
"fs-lib",
"img-lib",
"shutdown",
"uefi-lib",
}
if arch == distro.X86_64ArchName {
modules = append(modules, "biosdevname")
}
modules = append(modules, additionalModules...)
return &osbuild.DracutStageOptions{
Kernel: kernel,
Modules: modules,
Install: []string{"/.buildstamp"},
}
}
func bootISOMonoStageOptions(kernelVer, arch, vendor, product, osVersion, isolabel string) *osbuild.BootISOMonoStageOptions {
comprOptions := new(osbuild.FSCompressionOptions)
if bcj := osbuild.BCJOption(arch); bcj != "" {
comprOptions.BCJ = bcj
}
var architectures []string
if arch == distro.X86_64ArchName {
architectures = []string{"X64"}
} else if arch == distro.Aarch64ArchName {
architectures = []string{"AA64"}
} else {
panic("unsupported architecture")
}
return &osbuild.BootISOMonoStageOptions{
Product: osbuild.Product{
Name: product,
Version: osVersion,
},
ISOLabel: isolabel,
Kernel: kernelVer,
KernelOpts: fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", isolabel, kspath),
EFI: osbuild.EFI{
Architectures: architectures,
Vendor: vendor,
},
ISOLinux: osbuild.ISOLinux{
Enabled: arch == distro.X86_64ArchName,
Debug: false,
},
Templates: "99-generic",
RootFS: osbuild.RootFS{
Size: 9216,
Compression: osbuild.FSCompression{
Method: "xz",
Options: comprOptions,
},
},
}
}
func discinfoStageOptions(arch string) *osbuild.DiscinfoStageOptions {
return &osbuild.DiscinfoStageOptions{
BaseArch: arch,
Release: "202010217.n.0",
}
}
func xorrisofsStageOptions(filename, isolabel, arch string, isolinux bool) *osbuild.XorrisofsStageOptions {
options := &osbuild.XorrisofsStageOptions{
Filename: filename,
VolID: fmt.Sprintf(isolabel, arch),
SysID: "LINUX",
EFI: "images/efiboot.img",
ISOLevel: 3,
}
if isolinux {
options.Boot = &osbuild.XorrisofsBoot{
Image: "isolinux/isolinux.bin",
Catalog: "isolinux/boot.cat",
}
options.IsohybridMBR = "/usr/share/syslinux/isohdpfx.bin"
}
return options
}

View file

@ -0,0 +1,138 @@
package pipeline
import (
"github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
type AnacondaPipeline struct {
Pipeline
Repos []rpmmd.RepoConfig
PackageSpecs []rpmmd.PackageSpec
Users bool
KernelVer string
Arch string
Product string
OSVersion string
Variant string
Biosdevname bool
}
func NewAnacondaPipeline(buildPipeline *BuildPipeline) AnacondaPipeline {
return AnacondaPipeline{
Pipeline: New("anaconda-tree", &buildPipeline.Pipeline),
}
}
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.NewBuildstampStage(&osbuild2.BuildstampStageOptions{
Arch: p.Arch,
Product: p.Product,
Version: p.OSVersion,
Variant: p.Variant,
Final: true,
}))
pipeline.AddStage(osbuild2.NewLocaleStage(&osbuild2.LocaleStageOptions{Language: "en_US.UTF-8"}))
rootPassword := ""
rootUser := osbuild2.UsersStageOptionsUser{
Password: &rootPassword,
}
installUID := 0
installGID := 0
installHome := "/root"
installShell := "/usr/libexec/anaconda/run-anaconda"
installPassword := ""
installUser := osbuild2.UsersStageOptionsUser{
UID: &installUID,
GID: &installGID,
Home: &installHome,
Shell: &installShell,
Password: &installPassword,
}
usersStageOptions := &osbuild2.UsersStageOptions{
Users: map[string]osbuild2.UsersStageOptionsUser{
"root": rootUser,
"install": installUser,
},
}
pipeline.AddStage(osbuild2.NewUsersStage(usersStageOptions))
pipeline.AddStage(osbuild2.NewAnacondaStage(osbuild2.NewAnacondaStageOptions(p.Users)))
pipeline.AddStage(osbuild2.NewLoraxScriptStage(&osbuild2.LoraxScriptStageOptions{
Path: "99-generic/runtime-postinstall.tmpl",
BaseArch: p.Arch,
}))
pipeline.AddStage(osbuild2.NewDracutStage(dracutStageOptions(p.KernelVer, p.Biosdevname, []string{
"anaconda",
"rdma",
"rngd",
"multipath",
"fcoe",
"fcoe-uefi",
"iscsi",
"lunmask",
"nfs",
})))
pipeline.AddStage(osbuild2.NewSELinuxConfigStage(&osbuild2.SELinuxConfigStageOptions{State: osbuild2.SELinuxStatePermissive}))
return pipeline
}
func dracutStageOptions(kernelVer string, biosdevname bool, additionalModules []string) *osbuild2.DracutStageOptions {
kernel := []string{kernelVer}
modules := []string{
"bash",
"systemd",
"fips",
"systemd-initrd",
"modsign",
"nss-softokn",
"i18n",
"convertfs",
"network-manager",
"network",
"ifcfg",
"url-lib",
"drm",
"plymouth",
"crypt",
"dm",
"dmsquash-live",
"kernel-modules",
"kernel-modules-extra",
"kernel-network-modules",
"livenet",
"lvm",
"mdraid",
"qemu",
"qemu-net",
"resume",
"rootfs-block",
"terminfo",
"udev-rules",
"dracut-systemd",
"pollcdrom",
"usrmount",
"base",
"fs-lib",
"img-lib",
"shutdown",
"uefi-lib",
}
if biosdevname {
modules = append(modules, "biosdevname")
}
modules = append(modules, additionalModules...)
return &osbuild2.DracutStageOptions{
Kernel: kernel,
Modules: modules,
Install: []string{"/.buildstamp"},
}
}

View file

@ -0,0 +1,50 @@
package pipeline
import (
"github.com/osbuild/osbuild-composer/internal/osbuild2"
)
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),
treePipeline: treePipeline,
}
}
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.NewImplantisomd5Stage(&osbuild2.Implantisomd5StageOptions{Filename: p.Filename}))
return pipeline
}
func xorrisofsStageOptions(filename, isolabel string, isolinux bool) *osbuild2.XorrisofsStageOptions {
options := &osbuild2.XorrisofsStageOptions{
Filename: filename,
VolID: isolabel,
SysID: "LINUX",
EFI: "images/efiboot.img",
ISOLevel: 3,
}
if isolinux {
options.Boot = &osbuild2.XorrisofsBoot{
Image: "isolinux/isolinux.bin",
Catalog: "isolinux/boot.cat",
}
options.IsohybridMBR = "/usr/share/syslinux/isohdpfx.bin"
}
return options
}

View file

@ -0,0 +1,111 @@
package pipeline
import (
"fmt"
"path"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/osbuild2"
)
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
}
func NewISOTreePipeline(buildPipeline *BuildPipeline, treePipeline *Pipeline) ISOTreePipeline {
return ISOTreePipeline{
Pipeline: New("bootiso-tree", &buildPipeline.Pipeline),
treePipeline: treePipeline,
}
}
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())))
kickstartOptions, err := osbuild2.NewKickstartStageOptions(kspath, "", p.Users, p.Groups, makeISORootPath(ostreeRepoPath), p.OSTreeRef, p.OSName)
if err != nil {
panic("password encryption failed")
}
pipeline.AddStage(osbuild2.NewKickstartStage(kickstartOptions))
pipeline.AddStage(osbuild2.NewDiscinfoStage(&osbuild2.DiscinfoStageOptions{
BaseArch: p.Arch,
Release: p.Release,
}))
pipeline.AddStage(osbuild2.NewOSTreeInitStage(&osbuild2.OSTreeInitStageOptions{Path: ostreeRepoPath}))
pipeline.AddStage(osbuild2.NewOSTreePullStage(
&osbuild2.OSTreePullStageOptions{Repo: ostreeRepoPath},
osbuild2.NewOstreePullStageInputs("org.osbuild.source", p.OSTreeParent, p.OSTreeRef),
))
return pipeline
}
func bootISOMonoStageOptions(kernelVer, arch, vendor, product, osVersion, isolabel, kspath string) *osbuild2.BootISOMonoStageOptions {
comprOptions := new(osbuild2.FSCompressionOptions)
if bcj := osbuild2.BCJOption(arch); bcj != "" {
comprOptions.BCJ = bcj
}
var architectures []string
if arch == distro.X86_64ArchName {
architectures = []string{"X64"}
} else if arch == distro.Aarch64ArchName {
architectures = []string{"AA64"}
} else {
panic("unsupported architecture")
}
return &osbuild2.BootISOMonoStageOptions{
Product: osbuild2.Product{
Name: product,
Version: osVersion,
},
ISOLabel: isolabel,
Kernel: kernelVer,
KernelOpts: fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", isolabel, kspath),
EFI: osbuild2.EFI{
Architectures: architectures,
Vendor: vendor,
},
ISOLinux: osbuild2.ISOLinux{
Enabled: arch == distro.X86_64ArchName,
Debug: false,
},
Templates: "99-generic",
RootFS: osbuild2.RootFS{
Size: 9216,
Compression: osbuild2.FSCompression{
Method: "xz",
Options: comprOptions,
},
},
}
}
//makeISORootPath return a path that can be used to address files and folders in
//the root of the iso
func makeISORootPath(p string) string {
fullpath := path.Join("/run/install/repo", p)
return fmt.Sprintf("file://%s", fullpath)
}