From e844453c857dfb116cb045eac73199645d27f264 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Fri, 8 Jul 2022 15:28:14 +0100 Subject: [PATCH] manifest: make filename optional and generalise Tree We have three kinds of operating system trees, until we unify them to one, hide them behind one interface. Use this to read the architecture from the Tree rather than pass it in as a string to parent pipelines. Also, make the filename parameter optional in a few places, there should be no reason to set this rather than introspect it (except for backwards compatibility). Lastly, add another playground example sample to build a raw image. --- cmd/osbuild-playground/main.go | 16 ++--- cmd/osbuild-playground/my-container.go | 71 +++++++++++++++++++++ cmd/osbuild-playground/my-image.go | 70 ++++++++------------- cmd/osbuild-playground/partitiontables.go | 56 +++++++++++++++++ internal/distro/fedora/manifests.go | 77 ++++++++++++++++------- internal/manifest/anaconda.go | 24 ++++--- internal/manifest/commit_server_tree.go | 8 +++ internal/manifest/iso.go | 11 ++-- internal/manifest/iso_tree.go | 13 +++- internal/manifest/oci_container.go | 18 +++--- internal/manifest/os.go | 12 ++-- internal/manifest/pipeline.go | 11 ++++ internal/manifest/qcow2.go | 13 ++-- internal/manifest/raw.go | 18 +++--- internal/manifest/tar.go | 10 +-- internal/manifest/vmdk.go | 11 ++-- internal/manifest/vpc.go | 11 ++-- 17 files changed, 307 insertions(+), 143 deletions(-) create mode 100644 cmd/osbuild-playground/my-container.go create mode 100644 cmd/osbuild-playground/partitiontables.go diff --git a/cmd/osbuild-playground/main.go b/cmd/osbuild-playground/main.go index 1173d6912..f28c74f28 100644 --- a/cmd/osbuild-playground/main.go +++ b/cmd/osbuild-playground/main.go @@ -49,11 +49,11 @@ func findDnfJsonBin() string { func main() { var distroArg string - flag.StringVar(&distroArg, "distro", "", "distro to build from") + flag.StringVar(&distroArg, "distro", "host", "distro to build from") var archArg string flag.StringVar(&archArg, "arch", common.CurrentArch(), "architecture to build for") var imageTypeArg string - flag.StringVar(&imageTypeArg, "type", "my-image", "image type to build") + flag.StringVar(&imageTypeArg, "type", "my-container", "image type to build") flag.Parse() // Path to options or '-' for stdin @@ -83,16 +83,16 @@ func main() { distros := distroregistry.NewDefault() var d distro.Distro - if distroArg != "" { - d = distros.GetDistro(distroArg) - if d == nil { - panic(fmt.Sprintf("distro '%s' not supported\n", distroArg)) - } - } else { + if distroArg == "host" { d = distros.FromHost() if d == nil { panic("host distro not supported") } + } else { + d = distros.GetDistro(distroArg) + if d == nil { + panic(fmt.Sprintf("distro '%s' not supported\n", distroArg)) + } } arch, err := d.GetArch(archArg) diff --git a/cmd/osbuild-playground/my-container.go b/cmd/osbuild-playground/my-container.go new file mode 100644 index 000000000..3fb9aa106 --- /dev/null +++ b/cmd/osbuild-playground/my-container.go @@ -0,0 +1,71 @@ +package main + +import ( + "github.com/osbuild/osbuild-composer/internal/manifest" + "github.com/osbuild/osbuild-composer/internal/platform" + "github.com/osbuild/osbuild-composer/internal/rpmmd" + "github.com/osbuild/osbuild-composer/internal/runner" +) + +// MyContainer contains the arguments passed in as a JSON blob. +// You can replace them with whatever you want to use to +// configure your image. In the current example they are +// unused. +type MyContainer struct { + MyOption string `json:"my_option"` +} + +// Name returns the name of the image type, used to select what kind +// of image to build. +func (img *MyContainer) Name() string { + return "my-container" +} + +// init registeres this image type +func init() { + AddImageType(&MyContainer{}) +} + +// Build your manifest by attaching pipelines to it +// +// @m is the manifest you are constructing +// @options are what was passed in on the commandline +// @repos are the default repositories for the host OS/arch +// @runner is needed by any build pipelines +// +// Return nil when you are done, or an error if something +// went wrong. Your manifest will be streamed to osbuild +// for building. +func (img *MyContainer) InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner runner.Runner) error { + // Let's create a simple OCI container! + + // configure a build pipeline + build := manifest.NewBuild(m, runner, repos) + + // create a minimal non-bootable OS tree + os := manifest.NewOS(m, build, &platform.X86{}, repos) + + // create an OCI container containing the OS tree created above + manifest.NewOCIContainer(m, build, os) + + return nil +} + +// GetExports returns a list of the pipelines osbuild should export. +// These are the pipelines containing the artefact you want returned. +// +// TODO: Move this to be implemented in terms ofthe Manifest package. +// We should not need to know the pipeline names. +func (img *MyContainer) GetExports() []string { + return []string{"container"} +} + +// GetCheckpoints returns a list of the pipelines osbuild should +// checkpoint. These are the pipelines likely to be reusable in +// future runs. +// +// TODO: Move this to be implemented in terms ofthe Manifest package. +// We should not need to know the pipeline names. +func (img *MyContainer) GetCheckpoints() []string { + return []string{"build"} +} diff --git a/cmd/osbuild-playground/my-image.go b/cmd/osbuild-playground/my-image.go index b7294dff6..e87d8dba6 100644 --- a/cmd/osbuild-playground/my-image.go +++ b/cmd/osbuild-playground/my-image.go @@ -1,78 +1,62 @@ package main import ( + "math/rand" + + "github.com/osbuild/osbuild-composer/internal/disk" "github.com/osbuild/osbuild-composer/internal/manifest" "github.com/osbuild/osbuild-composer/internal/platform" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/runner" ) -func init() { - AddImageType(&MyImage{}) -} - -// MyImage contains the arguments passed in as a JSON blob. -// You can replace them with whatever you want to use to -// configure your image. In the current example they are -// unused. type MyImage struct { MyOption string `json:"my_option"` - Filename string `json:"filename"` } -// Name returns the name of the image type, used to select what kind -// of image to build. func (img *MyImage) Name() string { return "my-image" } -// Build your manifest by attaching pipelines to it -// -// @m is the manifest you are constructing -// @options are what was passed in on the commandline -// @repos are the default repositories for the host OS/arch -// @runner is needed by any build pipelines -// -// Return nil when you are done, or an error if something -// went wrong. Your manifest will be streamed to osbuild -// for building. +func init() { + AddImageType(&MyImage{}) +} + func (img *MyImage) InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner runner.Runner) error { - // Let's create a simple OCI container! + // Let's create a simple raw image! // configure a build pipeline build := manifest.NewBuild(m, runner, repos) - // create a non-bootable OS tree containing the `core` comps group - os := manifest.NewOS(m, build, &platform.X86{}, repos) - os.ExtraBasePackages = []string{ - "@core", + // create an x86_64 platform with bios boot + platform := &platform.X86{ + BIOS: true, } - filename := "my-container.tar" - if img.Filename != "" { - filename = img.Filename + // TODO: add helper + // math/rand is good enough in this case + /* #nosec G404 */ + pt, err := disk.NewPartitionTable(&basePT, nil, 0, false, rand.New(rand.NewSource(0))) + if err != nil { + panic(err) } - // create an OCI container containing the OS tree created above - manifest.NewOCIContainer(m, build, &os.Base, "x86_64", filename) + + // create a minimal bootable OS tree + os := manifest.NewOS(m, build, platform, repos) + os.PartitionTable = pt // we need a partition table + os.KernelName = "kernel" // use the default fedora kernel + + // create a raw image containing the OS tree created above + manifest.NewRawImage(m, build, os) return nil } -// GetExports returns a list of the pipelines osbuild should export. -// These are the pipelines containing the artefact you want returned. -// -// TODO: Move this to be implemented in terms ofthe Manifest package. -// We should not need to know the pipeline names. +// TODO: make internal func (img *MyImage) GetExports() []string { - return []string{"container"} + return []string{"image"} } -// GetCheckpoints returns a list of the pipelines osbuild should -// checkpoint. These are the pipelines likely to be reusable in -// future runs. -// -// TODO: Move this to be implemented in terms ofthe Manifest package. -// We should not need to know the pipeline names. func (img *MyImage) GetCheckpoints() []string { return []string{"build"} } diff --git a/cmd/osbuild-playground/partitiontables.go b/cmd/osbuild-playground/partitiontables.go new file mode 100644 index 000000000..b55a85626 --- /dev/null +++ b/cmd/osbuild-playground/partitiontables.go @@ -0,0 +1,56 @@ +package main + +import "github.com/osbuild/osbuild-composer/internal/disk" + +var basePT = disk.PartitionTable{ + UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0", + Type: "gpt", + Partitions: []disk.Partition{ + { + Size: 1048576, // 1MB + Bootable: true, + Type: disk.BIOSBootPartitionGUID, + UUID: disk.BIOSBootPartitionUUID, + }, + { + Size: 209715200, // 200 MB + Type: disk.EFISystemPartitionGUID, + UUID: disk.EFISystemPartitionUUID, + Payload: &disk.Filesystem{ + Type: "vfat", + UUID: disk.EFIFilesystemUUID, + Mountpoint: "/boot/efi", + Label: "EFI-SYSTEM", + FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt", + FSTabFreq: 0, + FSTabPassNo: 2, + }, + }, + { + Size: 524288000, // 500 MB + Type: disk.FilesystemDataGUID, + UUID: disk.FilesystemDataUUID, + Payload: &disk.Filesystem{ + Type: "ext4", + Mountpoint: "/boot", + Label: "boot", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, + }, + { + Size: 2147483648, // 2GiB + Type: disk.FilesystemDataGUID, + UUID: disk.RootPartitionUUID, + Payload: &disk.Filesystem{ + Type: "ext4", + Label: "root", + Mountpoint: "/", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, + }, + }, +} diff --git a/internal/distro/fedora/manifests.go b/internal/distro/fedora/manifests.go index 70ab50924..381e9e2d2 100644 --- a/internal/distro/fedora/manifests.go +++ b/internal/distro/fedora/manifests.go @@ -6,6 +6,7 @@ import ( "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/manifest" + "github.com/osbuild/osbuild-composer/internal/platform" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/workload" ) @@ -24,8 +25,9 @@ func qcow2Manifest(m *manifest.Manifest, if err != nil { return err } - imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline, "disk.img") - qcow2Pipeline := manifest.NewQCOW2(m, buildPipeline, imagePipeline, t.filename) + imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline) + qcow2Pipeline := manifest.NewQCOW2(m, buildPipeline, imagePipeline) + qcow2Pipeline.Filename = t.filename qcow2Pipeline.Compat = "1.1" return nil @@ -45,8 +47,9 @@ func vhdManifest(m *manifest.Manifest, if err != nil { return err } - imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline, "disk.img") - manifest.NewVPC(m, buildPipeline, imagePipeline, t.filename) + imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline) + p := manifest.NewVPC(m, buildPipeline, imagePipeline) + p.Filename = t.filename return nil } @@ -65,8 +68,9 @@ func vmdkManifest(m *manifest.Manifest, if err != nil { return err } - imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline, "disk.img") - manifest.NewVMDK(m, buildPipeline, imagePipeline, t.filename) + imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline) + p := manifest.NewVMDK(m, buildPipeline, imagePipeline) + p.Filename = t.filename return nil } @@ -85,8 +89,9 @@ func openstackManifest(m *manifest.Manifest, if err != nil { return err } - imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline, "disk.img") - manifest.NewQCOW2(m, buildPipeline, imagePipeline, t.filename) + imagePipeline := manifest.NewRawImage(m, buildPipeline, treePipeline) + p := manifest.NewQCOW2(m, buildPipeline, imagePipeline) + p.Filename = t.filename return nil } @@ -106,7 +111,8 @@ func ec2CommonManifest(m *manifest.Manifest, if err != nil { return nil } - manifest.NewRawImage(m, buildPipeline, treePipeline, diskfile) + p := manifest.NewRawImage(m, buildPipeline, treePipeline) + p.Filename = diskfile return nil } @@ -137,7 +143,15 @@ func iotInstallerManifest(m *manifest.Manifest, d := t.arch.distro ksUsers := len(customizations.GetUsers())+len(customizations.GetGroups()) > 0 - anacondaTreePipeline := anacondaTreePipeline(m, buildPipeline, repos, packageSets[installerPkgsKey], t.Arch().Name(), d.product, d.osVersion, "IoT", ksUsers) + anacondaTreePipeline := anacondaTreePipeline(m, + buildPipeline, + t.platform, + repos, + packageSets[installerPkgsKey], + d.product, + d.osVersion, + "IoT", + ksUsers) isoTreePipeline := bootISOTreePipeline(m, buildPipeline, anacondaTreePipeline, options, d.vendor, d.isolabelTmpl, customizations.GetUsers(), customizations.GetGroups()) bootISOPipeline(m, buildPipeline, isoTreePipeline, t.Filename(), false) @@ -176,7 +190,8 @@ func iotCommitManifest(m *manifest.Manifest, if err != nil { return err } - manifest.NewTar(m, buildPipeline, &commitPipeline.Base, "commit-archive", t.Filename()) + p := manifest.NewTar(m, buildPipeline, &commitPipeline.Base, "commit-archive") + p.Filename = t.Filename() return nil } @@ -196,8 +211,17 @@ func iotContainerManifest(m *manifest.Manifest, nginxConfigPath := "/etc/nginx.conf" httpPort := "8080" - containerTreePipeline := containerTreePipeline(m, buildPipeline, commitPipeline, repos, packageSets[containerPkgsKey], options, customizations, nginxConfigPath, httpPort) - containerPipeline(m, buildPipeline, &containerTreePipeline.Base, t, nginxConfigPath, httpPort) + containerTreePipeline := containerTreePipeline(m, + buildPipeline, + commitPipeline, + t.platform, + repos, + packageSets[containerPkgsKey], + options, + customizations, + nginxConfigPath, + httpPort) + containerPipeline(m, buildPipeline, containerTreePipeline, t, nginxConfigPath, httpPort) return nil } @@ -216,7 +240,8 @@ func containerManifest(m *manifest.Manifest, if err != nil { return err } - manifest.NewOCIContainer(m, buildPipeline, &treePipeline.Base, t.Arch().Name(), t.Filename()) + ociPipeline := manifest.NewOCIContainer(m, buildPipeline, treePipeline) + ociPipeline.Filename = t.Filename() return nil } @@ -260,14 +285,14 @@ func osPipeline(m *manifest.Manifest, } if t.rpmOstree { - var parent *manifest.OSPipelineOSTreeParent + var parent *manifest.OSTreeParent if options.OSTree.Parent != "" && options.OSTree.URL != "" { - parent = &manifest.OSPipelineOSTreeParent{ + parent = &manifest.OSTreeParent{ Checksum: options.OSTree.Parent, URL: options.OSTree.URL, } } - pl.OSTree = &manifest.OSPipelineOSTree{ + pl.OSTree = &manifest.OSTree{ Parent: parent, } } @@ -359,13 +384,14 @@ func ostreeCommitPipeline(m *manifest.Manifest, func containerTreePipeline(m *manifest.Manifest, buildPipeline *manifest.Build, commitPipeline *manifest.OSTreeCommit, + platform platform.Platform, repos []rpmmd.RepoConfig, containerPackageSet rpmmd.PackageSet, options distro.ImageOptions, c *blueprint.Customizations, nginxConfigPath, listenPort string) *manifest.OSTreeCommitServer { - p := manifest.NewOSTreeCommitServer(m, buildPipeline, repos, commitPipeline, nginxConfigPath, listenPort) + p := manifest.NewOSTreeCommitServer(m, buildPipeline, platform, repos, commitPipeline, nginxConfigPath, listenPort) p.ExtraPackages = containerPackageSet.Include p.ExtraRepos = containerPackageSet.Repositories language, _ := c.GetPrimaryLocale() @@ -377,11 +403,12 @@ func containerTreePipeline(m *manifest.Manifest, func containerPipeline(m *manifest.Manifest, buildPipeline *manifest.Build, - treePipeline *manifest.Base, + treePipeline manifest.Tree, t *imageType, nginxConfigPath, listenPort string) *manifest.OCIContainer { - p := manifest.NewOCIContainer(m, buildPipeline, treePipeline, t.Arch().Name(), t.Filename()) + p := manifest.NewOCIContainer(m, buildPipeline, treePipeline) + p.Filename = t.Filename() p.Cmd = []string{"nginx", "-c", nginxConfigPath} p.ExposedPorts = []string{listenPort} return p @@ -389,17 +416,18 @@ func containerPipeline(m *manifest.Manifest, func anacondaTreePipeline(m *manifest.Manifest, buildPipeline *manifest.Build, + pf platform.Platform, repos []rpmmd.RepoConfig, installerPackageSet rpmmd.PackageSet, - arch, product, osVersion, variant string, + product, osVersion, variant string, users bool) *manifest.Anaconda { - p := manifest.NewAnaconda(m, buildPipeline, repos, "kernel", arch, product, osVersion) + p := manifest.NewAnaconda(m, buildPipeline, pf, repos, "kernel", product, osVersion) p.ExtraPackages = installerPackageSet.Include p.ExtraRepos = installerPackageSet.Repositories p.Users = users p.Variant = variant - p.Biosdevname = (arch == distro.X86_64ArchName) + p.Biosdevname = (pf.GetArch() == platform.ARCH_X86_64) return p } @@ -426,7 +454,8 @@ func bootISOPipeline(m *manifest.Manifest, treePipeline *manifest.ISOTree, filename string, isolinux bool) *manifest.ISO { - p := manifest.NewISO(m, buildPipeline, treePipeline, filename) + p := manifest.NewISO(m, buildPipeline, treePipeline) p.ISOLinux = isolinux + p.Filename = filename return p } diff --git a/internal/manifest/anaconda.go b/internal/manifest/anaconda.go index 2ed8c0cd0..4cc8f5664 100644 --- a/internal/manifest/anaconda.go +++ b/internal/manifest/anaconda.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/platform" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) @@ -29,11 +30,11 @@ type Anaconda struct { // Variant is the variant of the product being installed, if applicable. Variant string + platform platform.Platform repos []rpmmd.RepoConfig packageSpecs []rpmmd.PackageSpec kernelName string kernelVer string - arch string product string version string } @@ -46,16 +47,16 @@ type Anaconda struct { // installer for. func NewAnaconda(m *Manifest, buildPipeline *Build, + platform platform.Platform, repos []rpmmd.RepoConfig, kernelName, - arch, product, version string) *Anaconda { p := &Anaconda{ Base: NewBase(m, "anaconda-tree", buildPipeline), + platform: platform, repos: repos, kernelName: kernelName, - arch: arch, product: product, version: version, } @@ -74,9 +75,8 @@ func (p *Anaconda) anacondaBootPackageSet() []string { "efibootmgr", } - // TODO: use constants for architectures - switch p.arch { - case "x86_64": + switch p.platform.GetArch() { + case platform.ARCH_X86_64: packages = append(packages, "grub2-efi-x64", "grub2-efi-x64-cdboot", @@ -86,14 +86,14 @@ func (p *Anaconda) anacondaBootPackageSet() []string { "syslinux", "syslinux-nonlinux", ) - case "aarch64": + case platform.ARCH_AARCH64: packages = append(packages, "grub2-efi-aa64-cdboot", "grub2-efi-aa64", "shim-aa64", ) default: - panic(fmt.Sprintf("unsupported arch: %s", p.arch)) + panic(fmt.Sprintf("unsupported arch: %s", p.platform.GetArch())) } return packages @@ -148,7 +148,7 @@ func (p *Anaconda) serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewRPMStage(osbuild2.NewRPMStageOptions(p.repos), osbuild2.NewRpmStageSourceFilesInputs(p.packageSpecs))) pipeline.AddStage(osbuild2.NewBuildstampStage(&osbuild2.BuildstampStageOptions{ - Arch: p.arch, + Arch: p.platform.GetArch().String(), Product: p.product, Variant: p.Variant, Version: p.version, @@ -184,7 +184,7 @@ func (p *Anaconda) 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.platform.GetArch().String(), })) pipeline.AddStage(osbuild2.NewDracutStage(dracutStageOptions(p.kernelVer, p.Biosdevname, []string{ "anaconda", @@ -255,3 +255,7 @@ func dracutStageOptions(kernelVer string, biosdevname bool, additionalModules [] Install: []string{"/.buildstamp"}, } } + +func (p *Anaconda) GetPlatform() platform.Platform { + return p.platform +} diff --git a/internal/manifest/commit_server_tree.go b/internal/manifest/commit_server_tree.go index 0a235b405..fa9045197 100644 --- a/internal/manifest/commit_server_tree.go +++ b/internal/manifest/commit_server_tree.go @@ -5,6 +5,7 @@ import ( "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/platform" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) @@ -20,6 +21,7 @@ type OSTreeCommitServer struct { // TODO: should this be configurable? Language string + platform platform.Platform repos []rpmmd.RepoConfig packageSpecs []rpmmd.PackageSpec commitPipeline *OSTreeCommit @@ -34,12 +36,14 @@ type OSTreeCommitServer struct { // nginx will be listening on. func NewOSTreeCommitServer(m *Manifest, buildPipeline *Build, + platform platform.Platform, repos []rpmmd.RepoConfig, commitPipeline *OSTreeCommit, nginxConfigPath, listenPort string) *OSTreeCommitServer { p := &OSTreeCommitServer{ Base: NewBase(m, "container-tree", buildPipeline), + platform: platform, repos: repos, commitPipeline: commitPipeline, nginxConfigPath: nginxConfigPath, @@ -139,3 +143,7 @@ func chmodStageOptions(path, mode string, recursive bool) *osbuild2.ChmodStageOp }, } } + +func (p *OSTreeCommitServer) GetPlatform() platform.Platform { + return p.platform +} diff --git a/internal/manifest/iso.go b/internal/manifest/iso.go index ec19704b1..a05138a86 100644 --- a/internal/manifest/iso.go +++ b/internal/manifest/iso.go @@ -9,19 +9,18 @@ import ( type ISO struct { Base ISOLinux bool + Filename string treePipeline *ISOTree - filename string } func NewISO(m *Manifest, buildPipeline *Build, - treePipeline *ISOTree, - filename string) *ISO { + treePipeline *ISOTree) *ISO { p := &ISO{ Base: NewBase(m, "bootiso", buildPipeline), treePipeline: treePipeline, - filename: filename, + Filename: "image.iso", } buildPipeline.addDependent(p) if treePipeline.Base.manifest != m { @@ -41,8 +40,8 @@ func (p *ISO) getBuildPackages() []string { func (p *ISO) serialize() osbuild2.Pipeline { pipeline := p.Base.serialize() - 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})) + 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/manifest/iso_tree.go b/internal/manifest/iso_tree.go index 32ac36984..cdb2d9e23 100644 --- a/internal/manifest/iso_tree.go +++ b/internal/manifest/iso_tree.go @@ -36,7 +36,7 @@ func NewISOTree(m *Manifest, osTreeRef, isoLabelTmpl string) *ISOTree { // TODO: replace isoLabelTmpl with more high-level properties - isoLabel := fmt.Sprintf(isoLabelTmpl, anacondaPipeline.arch) + isoLabel := fmt.Sprintf(isoLabelTmpl, anacondaPipeline.platform.GetArch()) p := &ISOTree{ Base: NewBase(m, "bootiso-tree", buildPipeline), @@ -77,7 +77,14 @@ func (p *ISOTree) serialize() osbuild2.Pipeline { kspath := "/osbuild.ks" ostreeRepoPath := "/ostree/repo" - pipeline.AddStage(osbuild2.NewBootISOMonoStage(bootISOMonoStageOptions(p.anacondaPipeline.kernelVer, p.anacondaPipeline.arch, p.UEFIVendor, p.anacondaPipeline.product, p.anacondaPipeline.version, p.isoLabel, kspath), osbuild2.NewBootISOMonoStagePipelineTreeInputs(p.anacondaPipeline.Name()))) + pipeline.AddStage(osbuild2.NewBootISOMonoStage(bootISOMonoStageOptions(p.anacondaPipeline.kernelVer, + p.anacondaPipeline.platform.GetArch().String(), + p.UEFIVendor, + 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 { @@ -86,7 +93,7 @@ func (p *ISOTree) serialize() osbuild2.Pipeline { pipeline.AddStage(osbuild2.NewKickstartStage(kickstartOptions)) pipeline.AddStage(osbuild2.NewDiscinfoStage(&osbuild2.DiscinfoStageOptions{ - BaseArch: p.anacondaPipeline.arch, + BaseArch: p.anacondaPipeline.platform.GetArch().String(), Release: p.Release, })) diff --git a/internal/manifest/oci_container.go b/internal/manifest/oci_container.go index 76e4e175b..bcd46fe39 100644 --- a/internal/manifest/oci_container.go +++ b/internal/manifest/oci_container.go @@ -8,26 +8,22 @@ import ( // tree created by another Pipeline. type OCIContainer struct { Base + Filename string Cmd []string ExposedPorts []string - treePipeline *Base - architecture string - filename string + treePipeline Tree } func NewOCIContainer(m *Manifest, buildPipeline *Build, - treePipeline *Base, - architecture, - filename string) *OCIContainer { + treePipeline Tree) *OCIContainer { p := &OCIContainer{ Base: NewBase(m, "container", buildPipeline), treePipeline: treePipeline, - architecture: architecture, - filename: filename, + Filename: "oci-archive.tar", } - if treePipeline.build.Base.manifest != m { + if treePipeline.GetManifest() != m { panic("tree pipeline from different manifest") } buildPipeline.addDependent(p) @@ -39,8 +35,8 @@ func (p *OCIContainer) serialize() osbuild2.Pipeline { pipeline := p.Base.serialize() options := &osbuild2.OCIArchiveStageOptions{ - Architecture: p.architecture, - Filename: p.filename, + Architecture: p.treePipeline.GetPlatform().GetArch().String(), + Filename: p.Filename, Config: &osbuild2.OCIArchiveConfig{ Cmd: p.Cmd, ExposedPorts: p.ExposedPorts, diff --git a/internal/manifest/os.go b/internal/manifest/os.go index 374da7d4b..b759c5f12 100644 --- a/internal/manifest/os.go +++ b/internal/manifest/os.go @@ -15,11 +15,11 @@ import ( "github.com/osbuild/osbuild-composer/internal/workload" ) -type OSPipelineOSTree struct { - Parent *OSPipelineOSTreeParent +type OSTree struct { + Parent *OSTreeParent } -type OSPipelineOSTreeParent struct { +type OSTreeParent struct { Checksum string URL string } @@ -43,7 +43,7 @@ type OS struct { // Workload to install on top of the base system Workload workload.Workload // OSTree configuration, if nil the tree cannot be in an OSTree commit - OSTree *OSPipelineOSTree + OSTree *OSTree // Partition table, if nil the tree cannot be put on a partioned disk PartitionTable *disk.PartitionTable // KernelName indicates that a kernel is installed, and names the kernel @@ -471,3 +471,7 @@ func usersFirstBootOptions(usersStageOptions *osbuild2.UsersStageOptions) *osbui return options } + +func (p *OS) GetPlatform() platform.Platform { + return p.platform +} diff --git a/internal/manifest/pipeline.go b/internal/manifest/pipeline.go index 244f59152..09a1e9256 100644 --- a/internal/manifest/pipeline.go +++ b/internal/manifest/pipeline.go @@ -8,6 +8,7 @@ package manifest import ( "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/platform" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) @@ -38,6 +39,10 @@ func (p Base) Name() string { return p.name } +func (p Base) GetManifest() *Manifest { + return p.manifest +} + func (p Base) getBuildPackages() []string { return []string{} } @@ -102,3 +107,9 @@ func (p Base) serialize() osbuild2.Pipeline { } return pipeline } + +type Tree interface { + Name() string + GetManifest() *Manifest + GetPlatform() platform.Platform +} diff --git a/internal/manifest/qcow2.go b/internal/manifest/qcow2.go index 2df7dcd46..8ac0f2eb2 100644 --- a/internal/manifest/qcow2.go +++ b/internal/manifest/qcow2.go @@ -7,10 +7,10 @@ import ( // A QCOW2 turns a raw image file into qcow2 image. type QCOW2 struct { Base - Compat string + Filename string + Compat string imgPipeline *RawImage - filename string } // NewQCOW2 createsa new QCOW2 pipeline. imgPipeline is the pipeline producing the @@ -18,12 +18,11 @@ type QCOW2 struct { // of the produced qcow2 image. func NewQCOW2(m *Manifest, buildPipeline *Build, - imgPipeline *RawImage, - filename string) *QCOW2 { + imgPipeline *RawImage) *QCOW2 { p := &QCOW2{ Base: NewBase(m, "qcow2", buildPipeline), imgPipeline: imgPipeline, - filename: filename, + Filename: "image.qcow2", } if imgPipeline.Base.manifest != m { panic("live image pipeline from different manifest") @@ -37,12 +36,12 @@ func (p *QCOW2) serialize() osbuild2.Pipeline { pipeline := p.Base.serialize() pipeline.AddStage(osbuild2.NewQEMUStage( - osbuild2.NewQEMUStageOptions(p.filename, + osbuild2.NewQEMUStageOptions(p.Filename, osbuild2.QEMUFormatQCOW2, osbuild2.QCOW2Options{ Compat: p.Compat, }), - osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.filename), + osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.Filename), )) return pipeline diff --git a/internal/manifest/raw.go b/internal/manifest/raw.go index 9afa090e4..f8a99177d 100644 --- a/internal/manifest/raw.go +++ b/internal/manifest/raw.go @@ -10,18 +10,16 @@ import ( type RawImage struct { Base treePipeline *OS - - filename string + Filename string } func NewRawImage(m *Manifest, buildPipeline *Build, - treePipeline *OS, - filename string) *RawImage { + treePipeline *OS) *RawImage { p := &RawImage{ Base: NewBase(m, "image", buildPipeline), treePipeline: treePipeline, - filename: filename, + Filename: "disk.img", } buildPipeline.addDependent(p) if treePipeline.Base.manifest != m { @@ -43,26 +41,26 @@ func (p *RawImage) serialize() osbuild2.Pipeline { panic("no partition table in live image") } - for _, stage := range osbuild2.GenImagePrepareStages(pt, p.filename, osbuild2.PTSfdisk) { + for _, stage := range osbuild2.GenImagePrepareStages(pt, p.Filename, osbuild2.PTSfdisk) { pipeline.AddStage(stage) } inputName := "root-tree" - copyOptions, copyDevices, copyMounts := osbuild2.GenCopyFSTreeOptions(inputName, p.treePipeline.Name(), p.filename, pt) + copyOptions, copyDevices, copyMounts := osbuild2.GenCopyFSTreeOptions(inputName, p.treePipeline.Name(), p.Filename, pt) copyInputs := osbuild2.NewCopyStagePipelineTreeInputs(inputName, p.treePipeline.Name()) pipeline.AddStage(osbuild2.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts)) - for _, stage := range osbuild2.GenImageFinishStages(pt, p.filename) { + for _, stage := range osbuild2.GenImageFinishStages(pt, p.Filename) { pipeline.AddStage(stage) } switch p.treePipeline.platform.GetArch() { case platform.ARCH_S390X: - loopback := osbuild2.NewLoopbackDevice(&osbuild2.LoopbackDeviceOptions{Filename: p.filename}) + loopback := osbuild2.NewLoopbackDevice(&osbuild2.LoopbackDeviceOptions{Filename: p.Filename}) pipeline.AddStage(osbuild2.NewZiplInstStage(osbuild2.NewZiplInstStageOptions(p.treePipeline.kernelVer, pt), loopback, copyDevices, copyMounts)) default: if grubLegacy := p.treePipeline.platform.GetBIOSPlatform(); grubLegacy != "" { - pipeline.AddStage(osbuild2.NewGrub2InstStage(osbuild2.NewGrub2InstStageOption(p.filename, pt, grubLegacy))) + pipeline.AddStage(osbuild2.NewGrub2InstStage(osbuild2.NewGrub2InstStageOption(p.Filename, pt, grubLegacy))) } } diff --git a/internal/manifest/tar.go b/internal/manifest/tar.go index d27e9e19a..eae9edae5 100644 --- a/internal/manifest/tar.go +++ b/internal/manifest/tar.go @@ -7,8 +7,9 @@ import ( // A Tar represents the contents of another pipeline in a tar file type Tar struct { Base + Filename string + inputPipeline *Base - filename string } // NewTar creates a new TarPipeline. The inputPipeline represents the @@ -17,12 +18,11 @@ type Tar struct { func NewTar(m *Manifest, buildPipeline *Build, inputPipeline *Base, - pipelinename, - filename string) *Tar { + pipelinename string) *Tar { p := &Tar{ Base: NewBase(m, pipelinename, buildPipeline), inputPipeline: inputPipeline, - filename: filename, + Filename: "image.tar", } if inputPipeline.manifest != m { panic("tree pipeline from different manifest") @@ -39,7 +39,7 @@ func (p *Tar) serialize() osbuild2.Pipeline { tree.Type = "org.osbuild.tree" tree.Origin = "org.osbuild.pipeline" tree.References = []string{"name:" + p.inputPipeline.Name()} - tarStage := osbuild2.NewTarStage(&osbuild2.TarStageOptions{Filename: p.filename}, &osbuild2.TarStageInputs{Tree: tree}) + tarStage := osbuild2.NewTarStage(&osbuild2.TarStageOptions{Filename: p.Filename}, &osbuild2.TarStageInputs{Tree: tree}) pipeline.AddStage(tarStage) diff --git a/internal/manifest/vmdk.go b/internal/manifest/vmdk.go index 98be3274b..94981fa68 100644 --- a/internal/manifest/vmdk.go +++ b/internal/manifest/vmdk.go @@ -7,21 +7,20 @@ import ( // A VMDK turns a raw image file into vmdk image. type VMDK struct { Base + Filename string imgPipeline *RawImage - filename string } // NewVMDK creates a new VMDK pipeline. imgPipeline is the pipeline producing the // raw image. Filename is the name of the produced image. func NewVMDK(m *Manifest, buildPipeline *Build, - imgPipeline *RawImage, - filename string) *VMDK { + imgPipeline *RawImage) *VMDK { p := &VMDK{ Base: NewBase(m, "vmdk", buildPipeline), imgPipeline: imgPipeline, - filename: filename, + Filename: "image.vmdk", } if imgPipeline.Base.manifest != m { panic("live image pipeline from different manifest") @@ -35,10 +34,10 @@ func (p *VMDK) serialize() osbuild2.Pipeline { pipeline := p.Base.serialize() pipeline.AddStage(osbuild2.NewQEMUStage( - osbuild2.NewQEMUStageOptions(p.filename, osbuild2.QEMUFormatVMDK, osbuild2.VMDKOptions{ + osbuild2.NewQEMUStageOptions(p.Filename, osbuild2.QEMUFormatVMDK, osbuild2.VMDKOptions{ Subformat: osbuild2.VMDKSubformatStreamOptimized, }), - osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.filename), + osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.Filename), )) return pipeline diff --git a/internal/manifest/vpc.go b/internal/manifest/vpc.go index 1d4b34633..7ecc4027e 100644 --- a/internal/manifest/vpc.go +++ b/internal/manifest/vpc.go @@ -7,9 +7,9 @@ import ( // A VPC turns a raw image file into qemu-based image format, such as qcow2. type VPC struct { Base + Filename string imgPipeline *RawImage - filename string } // NewVPC createsa new Qemu pipeline. imgPipeline is the pipeline producing the @@ -17,12 +17,11 @@ type VPC struct { // of the produced image. func NewVPC(m *Manifest, buildPipeline *Build, - imgPipeline *RawImage, - filename string) *VPC { + imgPipeline *RawImage) *VPC { p := &VPC{ Base: NewBase(m, "vpc", buildPipeline), imgPipeline: imgPipeline, - filename: filename, + Filename: "image.vpc", } if imgPipeline.Base.manifest != m { panic("live image pipeline from different manifest") @@ -36,8 +35,8 @@ func (p *VPC) serialize() osbuild2.Pipeline { pipeline := p.Base.serialize() pipeline.AddStage(osbuild2.NewQEMUStage( - osbuild2.NewQEMUStageOptions(p.filename, osbuild2.QEMUFormatVPC, nil), - osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.filename), + osbuild2.NewQEMUStageOptions(p.Filename, osbuild2.QEMUFormatVPC, nil), + osbuild2.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.Filename), )) return pipeline