package rhel90 import ( "fmt" "math/rand" "path" "path/filepath" "strings" "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/disk" "github.com/osbuild/osbuild-composer/internal/distro" osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" "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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) diskfile := "disk.img" imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, "qcow2", "1.1") pipelines = append(pipelines, *qemuPipeline) return pipelines, nil } func prependKernelCmdlineStage(pipeline *osbuild.Pipeline, t *imageType, pt *disk.PartitionTable) *osbuild.Pipeline { rootFsUUID := pt.RootPartition().Filesystem.UUID kernelStage := osbuild.NewKernelCmdlineStage(kernelCmdlineStageOptions(rootFsUUID, t.kernelOptions)) pipeline.Stages = append([]*osbuild.Stage{kernelStage}, pipeline.Stages...) return pipeline } 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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) diskfile := "disk.img" imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, "vpc", "") pipelines = append(pipelines, *qemuPipeline) return pipelines, nil } 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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) diskfile := "disk.img" imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, "vmdk", "") pipelines = append(pipelines, *qemuPipeline) return pipelines, nil } 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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) diskfile := "disk.img" imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, "qcow2", "") pipelines = append(pipelines, *qemuPipeline) return pipelines, nil } func ec2CommonPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand, diskfile string) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) // The last stage must be the SELinux stage treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) return pipelines, nil } func ec2SapPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand, diskfile string) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) partitionTable, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, &partitionTable) if err != nil { return nil, err } // SAP-specific configuration treePipeline.AddStage(osbuild.NewSELinuxConfigStage(&osbuild.SELinuxConfigStageOptions{ State: osbuild.SELinuxStatePermissive, })) // RHBZ#1960617 treePipeline.AddStage(osbuild.NewTunedStage(osbuild.NewTunedStageOptions("sap-hana"))) // RHBZ#1959979 treePipeline.AddStage(osbuild.NewTmpfilesdStage(osbuild.NewTmpfilesdStageOptions("sap.conf", []osbuild.TmpfilesdConfigLine{ { Type: "x", Path: "/tmp/.sap*", }, { Type: "x", Path: "/tmp/.hdb*lock", }, { Type: "x", Path: "/tmp/.trex*lock", }, }, ))) // RHBZ#1959963 treePipeline.AddStage(osbuild.NewPamLimitsConfStage(osbuild.NewPamLimitsConfStageOptions("99-sap.conf", []osbuild.PamLimitsConfigLine{ { Domain: "@sapsys", Type: osbuild.PamLimitsTypeHard, Item: osbuild.PamLimitsItemNofile, Value: osbuild.PamLimitsValueInt(65536), }, { Domain: "@sapsys", Type: osbuild.PamLimitsTypeSoft, Item: osbuild.PamLimitsItemNofile, Value: osbuild.PamLimitsValueInt(65536), }, { Domain: "@dba", Type: osbuild.PamLimitsTypeHard, Item: osbuild.PamLimitsItemNofile, Value: osbuild.PamLimitsValueInt(65536), }, { Domain: "@dba", Type: osbuild.PamLimitsTypeSoft, Item: osbuild.PamLimitsItemNofile, Value: osbuild.PamLimitsValueInt(65536), }, { Domain: "@sapsys", Type: osbuild.PamLimitsTypeHard, Item: osbuild.PamLimitsItemNproc, Value: osbuild.PamLimitsValueUnlimited, }, { Domain: "@sapsys", Type: osbuild.PamLimitsTypeSoft, Item: osbuild.PamLimitsItemNproc, Value: osbuild.PamLimitsValueUnlimited, }, { Domain: "@dba", Type: osbuild.PamLimitsTypeHard, Item: osbuild.PamLimitsItemNproc, Value: osbuild.PamLimitsValueUnlimited, }, { Domain: "@dba", Type: osbuild.PamLimitsTypeSoft, Item: osbuild.PamLimitsItemNproc, Value: osbuild.PamLimitsValueUnlimited, }, }, ))) // RHBZ#1959962 treePipeline.AddStage(osbuild.NewSysctldStage(osbuild.NewSysctldStageOptions("sap.conf", []osbuild.SysctldConfigLine{ { Key: "kernel.pid_max", Value: "4194304", }, { Key: "vm.max_map_count", Value: "2147483647", }, }, ))) // E4S/EUS treePipeline.AddStage(osbuild.NewDNFConfigStage(osbuild.NewDNFConfigStageOptions( []osbuild.DNFVariable{ { Name: "releasever", Value: t.arch.distro.osVersion, }, }, nil, ))) treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable) treePipeline.AddStage(osbuild.NewFSTabStage(partitionTable.FSTabStageOptionsV2())) kernelVer := kernelVerStr(packageSetSpecs[blueprintPkgsKey], customizations.GetKernel().Name, t.Arch().Name()) treePipeline.AddStage(bootloaderConfigStage(t, partitionTable, customizations.GetKernel(), kernelVer, false, false)) // The last stage must be the SELinux stage treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, &partitionTable, t.arch, kernelVer) pipelines = append(pipelines, *imagePipeline) return pipelines, nil } // ec2Pipelines returns pipelines which produce uncompressed EC2 images which are expected to use RHSM for content func ec2Pipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { return ec2CommonPipelines(t, customizations, options, repos, packageSetSpecs, rng, t.Filename()) } // rhelEc2Pipelines returns pipelines which produce XZ-compressed EC2 images which are expected to use RHUI for content func rhelEc2Pipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { rawImageFilename := "image.raw" pipelines, err := ec2CommonPipelines(t, customizations, options, repos, packageSetSpecs, rng, rawImageFilename) if err != nil { return nil, err } lastPipeline := pipelines[len(pipelines)-1] pipelines = append(pipelines, *xzArchivePipeline(lastPipeline.Name, rawImageFilename, t.Filename())) return pipelines, nil } // rhelEc2SapPipelines returns pipelines which produce XZ-compressed EC2 SAP images which are expected to use RHUI for content func rhelEc2SapPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { rawImageFilename := "image.raw" pipelines, err := ec2SapPipelines(t, customizations, options, repos, packageSetSpecs, rng, rawImageFilename) if err != nil { return nil, err } lastPipeline := pipelines[len(pipelines)-1] pipelines = append(pipelines, *xzArchivePipeline(lastPipeline.Name, rawImageFilename, t.Filename())) return pipelines, nil } func tarPipelines(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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, nil) if err != nil { return nil, err } treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) tarPipeline := osbuild.Pipeline{ Name: "root-tar", Build: "name:build", } tarPipeline.AddStage(tarStage("os", "root.tar.xz")) pipelines = append(pipelines, tarPipeline) return pipelines, nil } //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 edgeInstallerPipelines(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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) installerPackages := packageSetSpecs[installerPkgsKey] d := t.arch.distro archName := t.arch.name kernelVer := kernelVerStr(installerPackages, "kernel", archName) ostreeRepoPath := "/ostree/repo" payloadStages := ostreePayloadStages(options, ostreeRepoPath) kickstartOptions := ostreeKickstartStageOptions(makeISORootPath(ostreeRepoPath), options.OSTree.Ref) pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "edge")) 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)) return pipelines, nil } func tarInstallerPipelines(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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options, nil) if err != nil { return nil, err } treePipeline.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) pipelines = append(pipelines, *treePipeline) kernelPkg := new(rpmmd.PackageSpec) installerPackages := packageSetSpecs[installerPkgsKey] for _, pkg := range installerPackages { if pkg.Name == "kernel" { // Implicit memory alasing doesn't couse any bug in this case /* #nosec G601 */ kernelPkg = &pkg break } } if kernelPkg == nil { return nil, fmt.Errorf("kernel package not found in installer package set") } kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch) tarPath := "/liveimg.tar" tarPayloadStages := []*osbuild.Stage{tarStage("os", tarPath)} kickstartOptions := tarKickstartStageOptions(makeISORootPath(tarPath)) archName := t.arch.name d := t.arch.distro pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "BaseOS")) isolabel := fmt.Sprintf(d.isolabelTmpl, archName) pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, archName, d.vendor, d.product, d.osVersion, isolabel, kickstartOptions, tarPayloadStages)) pipelines = append(pipelines, *bootISOPipeline(t.Filename(), d.isolabelTmpl, t.Arch().Name(), true)) return pipelines, nil } func edgeCorePipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec) ([]osbuild.Pipeline, error) { pipelines := make([]osbuild.Pipeline, 0) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) treePipeline, err := ostreeTreePipeline(t, repos, packageSetSpecs[osPkgsKey], packageSetSpecs[blueprintPkgsKey], customizations, options) if err != nil { return nil, err } pipelines = append(pipelines, *treePipeline) pipelines = append(pipelines, *ostreeCommitPipeline(options, t.arch.distro.osVersion)) return pipelines, nil } func edgeCommitPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines, err := edgeCorePipelines(t, customizations, options, repos, packageSetSpecs) if err != nil { return nil, err } tarPipeline := osbuild.Pipeline{ Name: "commit-archive", Build: "name:build", } tarPipeline.AddStage(tarStage("ostree-commit", t.Filename())) pipelines = append(pipelines, tarPipeline) return pipelines, nil } func edgeContainerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) { pipelines, err := edgeCorePipelines(t, customizations, options, repos, packageSetSpecs) if err != nil { return nil, err } nginxConfigPath := "/etc/nginx.conf" httpPort := "8080" pipelines = append(pipelines, *containerTreePipeline(repos, packageSetSpecs[containerPkgsKey], options, customizations, nginxConfigPath, httpPort)) pipelines = append(pipelines, *containerPipeline(t, nginxConfigPath, httpPort)) return pipelines, nil } func edgeImagePipelines(t *imageType, filename string, options distro.ImageOptions, rng *rand.Rand) ([]osbuild.Pipeline, string, error) { pipelines := make([]osbuild.Pipeline, 0) ostreeRepoPath := "/ostree/repo" imgName := "image.raw" partitionTable, err := t.getPartitionTable(nil, options, rng) if err != nil { return nil, "", err } // prepare ostree deployment tree treePipeline := ostreeDeployPipeline(t, &partitionTable, ostreeRepoPath, nil, "", rng, options) pipelines = append(pipelines, *treePipeline) // make raw image from tree imagePipeline := liveImagePipeline(treePipeline.Name, imgName, &partitionTable, t.arch, "") pipelines = append(pipelines, *imagePipeline) // compress image xzPipeline := xzArchivePipeline(imagePipeline.Name, imgName, filename) pipelines = append(pipelines, *xzPipeline) return pipelines, xzPipeline.Name, nil } func edgeRawImagePipelines(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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) imgName := t.filename // create the raw image imagePipelines, _, err := edgeImagePipelines(t, imgName, options, rng) if err != nil { return nil, err } pipelines = append(pipelines, imagePipelines...) return pipelines, nil } func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec, runner string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "build" p.Runner = runner p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(buildPackageSpecs))) p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(true))) return p } func osPipeline(t *imageType, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, bpPackages []rpmmd.PackageSpec, c *blueprint.Customizations, options distro.ImageOptions, pt *disk.PartitionTable) (*osbuild.Pipeline, error) { imageConfig := t.getDefaultImageConfig() p := new(osbuild.Pipeline) p.Name = "os" p.Build = "name:build" packages = append(packages, bpPackages...) p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) // If the /boot is on a separate partition, the prefix for the BLS stage must be "" if pt == nil || pt.BootPartition() == nil { p.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{})) } else { p.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{Prefix: common.StringToPtr("")})) } language, keyboard := c.GetPrimaryLocale() if language != nil { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language})) } else { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: imageConfig.Locale})) } if keyboard != nil { p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *keyboard})) } else if imageConfig.Keyboard != nil { p.AddStage(osbuild.NewKeymapStage(imageConfig.Keyboard)) } if hostname := c.GetHostname(); hostname != nil { p.AddStage(osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{Hostname: *hostname})) } timezone, ntpServers := c.GetTimezoneSettings() if timezone != nil { p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: *timezone})) } else { p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: imageConfig.Timezone})) } if len(ntpServers) > 0 { p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers})) } else if imageConfig.TimeSynchronization != nil { p.AddStage(osbuild.NewChronyStage(imageConfig.TimeSynchronization)) } if groups := c.GetGroups(); len(groups) > 0 { p.AddStage(osbuild.NewGroupsStage(groupStageOptions(groups))) } if users := c.GetUsers(); len(users) > 0 { userOptions, err := userStageOptions(users) if err != nil { return nil, err } p.AddStage(osbuild.NewUsersStage(userOptions)) } if services := c.GetServices(); services != nil || imageConfig.EnabledServices != nil || imageConfig.DisabledServices != nil || imageConfig.DefaultTarget != "" { p.AddStage(osbuild.NewSystemdStage(systemdStageOptions( imageConfig.EnabledServices, imageConfig.DisabledServices, services, imageConfig.DefaultTarget, ))) } if firewall := c.GetFirewall(); firewall != nil { p.AddStage(osbuild.NewFirewallStage(firewallStageOptions(firewall))) } for _, sysconfigConfig := range imageConfig.Sysconfig { p.AddStage(osbuild.NewSysconfigStage(sysconfigConfig)) } if t.arch.distro.isRHEL() { if options.Subscription != nil { commands := []string{ fmt.Sprintf("/usr/sbin/subscription-manager register --org=%s --activationkey=%s --serverurl %s --baseurl %s", options.Subscription.Organization, options.Subscription.ActivationKey, options.Subscription.ServerUrl, options.Subscription.BaseUrl), } if options.Subscription.Insights { commands = append(commands, "/usr/bin/insights-client --register") } p.AddStage(osbuild.NewFirstBootStage(&osbuild.FirstBootStageOptions{ Commands: commands, WaitForNetwork: true, })) if rhsmConfig, exists := imageConfig.RHSMConfig[distro.RHSMConfigWithSubscription]; exists { p.AddStage(osbuild.NewRHSMStage(rhsmConfig)) } } else { if rhsmConfig, exists := imageConfig.RHSMConfig[distro.RHSMConfigNoSubscription]; exists { p.AddStage(osbuild.NewRHSMStage(rhsmConfig)) } } } for _, systemdLogindConfig := range imageConfig.SystemdLogind { p.AddStage(osbuild.NewSystemdLogindStage(systemdLogindConfig)) } for _, cloudInitConfig := range imageConfig.CloudInit { p.AddStage(osbuild.NewCloudInitStage(cloudInitConfig)) } for _, modprobeConfig := range imageConfig.Modprobe { p.AddStage(osbuild.NewModprobeStage(modprobeConfig)) } for _, dracutConfConfig := range imageConfig.DracutConf { p.AddStage(osbuild.NewDracutConfStage(dracutConfConfig)) } for _, systemdUnitConfig := range imageConfig.SystemdUnit { p.AddStage(osbuild.NewSystemdUnitStage(systemdUnitConfig)) } if authselectConfig := imageConfig.Authselect; authselectConfig != nil { p.AddStage(osbuild.NewAuthselectStage(authselectConfig)) } return p, nil } func ostreeTreePipeline(t *imageType, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, bpPackages []rpmmd.PackageSpec, c *blueprint.Customizations, options distro.ImageOptions) (*osbuild.Pipeline, error) { imageConfig := t.getDefaultImageConfig() p := new(osbuild.Pipeline) p.Name = "ostree-tree" p.Build = "name:build" packages = append(packages, bpPackages...) if options.OSTree.Parent != "" && options.OSTree.URL != "" { p.AddStage(osbuild.NewOSTreePasswdStage("org.osbuild.source", options.OSTree.Parent)) } p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) p.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{})) language, keyboard := c.GetPrimaryLocale() if language != nil { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language})) } else { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: imageConfig.Locale})) } if keyboard != nil { p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *keyboard})) } else if imageConfig.Keyboard != nil { p.AddStage(osbuild.NewKeymapStage(imageConfig.Keyboard)) } if hostname := c.GetHostname(); hostname != nil { p.AddStage(osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{Hostname: *hostname})) } timezone, ntpServers := c.GetTimezoneSettings() if timezone != nil { p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: *timezone})) } else { p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: imageConfig.Timezone})) } if len(ntpServers) > 0 { p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers})) } else if imageConfig.TimeSynchronization != nil { p.AddStage(osbuild.NewChronyStage(imageConfig.TimeSynchronization)) } if groups := c.GetGroups(); len(groups) > 0 { p.AddStage(osbuild.NewGroupsStage(groupStageOptions(groups))) } if users := c.GetUsers(); len(users) > 0 { userOptions, err := userStageOptions(users) if err != nil { return nil, err } p.AddStage(osbuild.NewUsersStage(userOptions)) p.AddStage(osbuild.NewFirstBootStage(usersFirstBootOptions(userOptions))) } if services := c.GetServices(); services != nil || imageConfig.EnabledServices != nil || imageConfig.DisabledServices != nil || imageConfig.DefaultTarget != "" { p.AddStage(osbuild.NewSystemdStage(systemdStageOptions( imageConfig.EnabledServices, imageConfig.DisabledServices, services, imageConfig.DefaultTarget, ))) } if firewall := c.GetFirewall(); firewall != nil { p.AddStage(osbuild.NewFirewallStage(firewallStageOptions(firewall))) } for _, sysconfigConfig := range imageConfig.Sysconfig { p.AddStage(osbuild.NewSysconfigStage(sysconfigConfig)) } if t.arch.distro.isRHEL() { if options.Subscription != nil { commands := []string{ fmt.Sprintf("/usr/sbin/subscription-manager register --org=%s --activationkey=%s --serverurl %s --baseurl %s", options.Subscription.Organization, options.Subscription.ActivationKey, options.Subscription.ServerUrl, options.Subscription.BaseUrl), } if options.Subscription.Insights { commands = append(commands, "/usr/bin/insights-client --register") } p.AddStage(osbuild.NewFirstBootStage(&osbuild.FirstBootStageOptions{ Commands: commands, WaitForNetwork: true, })) if rhsmConfig, exists := imageConfig.RHSMConfig[distro.RHSMConfigWithSubscription]; exists { p.AddStage(osbuild.NewRHSMStage(rhsmConfig)) } } else { if rhsmConfig, exists := imageConfig.RHSMConfig[distro.RHSMConfigNoSubscription]; exists { p.AddStage(osbuild.NewRHSMStage(rhsmConfig)) } } } for _, systemdLogindConfig := range imageConfig.SystemdLogind { p.AddStage(osbuild.NewSystemdLogindStage(systemdLogindConfig)) } for _, cloudInitConfig := range imageConfig.CloudInit { p.AddStage(osbuild.NewCloudInitStage(cloudInitConfig)) } for _, modprobeConfig := range imageConfig.Modprobe { p.AddStage(osbuild.NewModprobeStage(modprobeConfig)) } for _, dracutConfConfig := range imageConfig.DracutConf { p.AddStage(osbuild.NewDracutConfStage(dracutConfConfig)) } for _, systemdUnitConfig := range imageConfig.SystemdUnit { p.AddStage(osbuild.NewSystemdUnitStage(systemdUnitConfig)) } if authselectConfig := imageConfig.Authselect; authselectConfig != nil { p.AddStage(osbuild.NewAuthselectStage(authselectConfig)) } p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) p.AddStage(osbuild.NewOSTreePrepTreeStage(&osbuild.OSTreePrepTreeStageOptions{ EtcGroupMembers: []string{ // NOTE: We may want to make this configurable. "wheel", "docker", }, })) return p, nil } func ostreeCommitPipeline(options distro.ImageOptions, osVersion string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "ostree-commit" p.Build = "name:build" p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/repo"})) commitStageInput := new(osbuild.OSTreeCommitStageInput) commitStageInput.Type = "org.osbuild.tree" commitStageInput.Origin = "org.osbuild.pipeline" commitStageInput.References = osbuild.OSTreeCommitStageReferences{"name:ostree-tree"} p.AddStage(osbuild.NewOSTreeCommitStage( &osbuild.OSTreeCommitStageOptions{ Ref: options.OSTree.Ref, OSVersion: osVersion, Parent: options.OSTree.Parent, }, &osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}), ) return p } func tarStage(source, filename string) *osbuild.Stage { tree := new(osbuild.TarStageInput) tree.Type = "org.osbuild.tree" tree.Origin = "org.osbuild.pipeline" tree.References = []string{"name:" + source} return osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: filename}, &osbuild.TarStageInputs{Tree: tree}) } func containerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, c *blueprint.Customizations, nginxConfigPath, listenPort string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "container-tree" p.Build = "name:build" p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) language, _ := c.GetPrimaryLocale() if language != nil { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language})) } else { p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US"})) } htmlRoot := "/usr/share/nginx/html" repoPath := filepath.Join(htmlRoot, "repo") p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: repoPath})) p.AddStage(osbuild.NewOSTreePullStage( &osbuild.OSTreePullStageOptions{Repo: repoPath}, ostreePullStageInputs("org.osbuild.pipeline", "name:ostree-commit", options.OSTree.Ref), )) // make nginx log and lib directories world writeable, otherwise nginx can't start in // an unprivileged container p.AddStage(osbuild.NewChmodStage(chmodStageOptions("/var/log/nginx", "a+rwX", true))) p.AddStage(osbuild.NewChmodStage(chmodStageOptions("/var/lib/nginx", "a+rwX", true))) p.AddStage(osbuild.NewNginxConfigStage(nginxConfigStageOptions(nginxConfigPath, htmlRoot, listenPort))) return p } func containerPipeline(t *imageType, nginxConfigPath, listenPort string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "container" p.Build = "name:build" options := &osbuild.OCIArchiveStageOptions{ Architecture: t.arch.Name(), Filename: t.Filename(), Config: &osbuild.OCIArchiveConfig{ Cmd: []string{"nginx", "-c", nginxConfigPath}, ExposedPorts: []string{listenPort}, }, } baseInput := new(osbuild.OCIArchiveStageInput) baseInput.Type = "org.osbuild.tree" baseInput.Origin = "org.osbuild.pipeline" baseInput.References = []string{"name:container-tree"} inputs := &osbuild.OCIArchiveStageInputs{Base: baseInput} p.AddStage(osbuild.NewOCIArchiveStage(options, inputs)) 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}, ostreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref), )) return stages } func edgeSimplifiedInstallerPipelines(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) pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner)) installerPackages := packageSetSpecs[installerPkgsKey] kernelVer := kernelVerStr(installerPackages, "kernel", t.Arch().Name()) imgName := "disk.img.xz" installDevice := customizations.GetInstallationDevice() // create the raw image imagePipelines, imgPipelineName, err := edgeImagePipelines(t, imgName, options, rng) if err != nil { return nil, err } pipelines = append(pipelines, imagePipelines...) // create boot ISO with raw image d := t.arch.distro archName := t.arch.name installerTreePipeline := simplifiedInstallerTreePipeline(repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "edge") isolabel := fmt.Sprintf(d.isolabelTmpl, archName) efibootTreePipeline := simplifiedInstallerEFIBootTreePipeline(installDevice, kernelVer, archName, d.vendor, d.product, d.osVersion, isolabel) bootISOTreePipeline, err := simplifiedInstallerBootISOTreePipeline(imgPipelineName, kernelVer, rng) if err != nil { return nil, err } pipelines = append(pipelines, *installerTreePipeline, *efibootTreePipeline, *bootISOTreePipeline) pipelines = append(pipelines, *bootISOPipeline(t.Filename(), d.isolabelTmpl, t.Arch().Name(), false)) return pipelines, nil } func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rng *rand.Rand) (*osbuild.Pipeline, error) { p := new(osbuild.Pipeline) p.Name = "bootiso-tree" p.Build = "name:build" p.AddStage(osbuild.NewCopyStageSimple( &osbuild.CopyStageOptions{ Paths: []osbuild.CopyStagePath{ { From: "input://file/disk.img.xz", To: "tree:///disk.img.xz", }, }, }, osbuild.NewFilesInputs(osbuild.NewFilesInputReferencesPipeline(archivePipelineName, "disk.img.xz")), )) p.AddStage(osbuild.NewMkdirStage( &osbuild.MkdirStageOptions{ Paths: []osbuild.Path{ { Path: "images", }, { Path: "images/pxeboot", }, }, }, )) var sectorSize uint64 = 512 // TODO: handle error volid, err := disk.NewRandomVolIDFromReader(rng) if err != nil { return nil, err } pt := disk.PartitionTable{ Size: 20971520, Partitions: []disk.Partition{ { Start: 0, Size: 20971520 / sectorSize, Filesystem: &disk.Filesystem{ Type: "vfat", Mountpoint: "/", UUID: volid, }, }, }, } filename := "images/efiboot.img" loopback := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: filename}) p.AddStage(osbuild.NewTruncateStage(&osbuild.TruncateStageOptions{Filename: filename, Size: fmt.Sprintf("%d", pt.Size)})) for _, stage := range mkfsStages(&pt, loopback) { p.AddStage(stage) } inputName := "root-tree" copyInputs := copyPipelineTreeInputs(inputName, "efiboot-tree") copyOptions, copyDevices, copyMounts := copyFSTreeOptions(inputName, "efiboot-tree", &pt, loopback) p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts)) inputName = "coi" copyInputs = copyPipelineTreeInputs(inputName, "coi-tree") p.AddStage(osbuild.NewCopyStageSimple( &osbuild.CopyStageOptions{ Paths: []osbuild.CopyStagePath{ { From: fmt.Sprintf("input://%s/boot/vmlinuz-%s", inputName, kver), To: "tree:///images/pxeboot/vmlinuz", }, { From: fmt.Sprintf("input://%s/boot/initramfs-%s.img", inputName, kver), To: "tree:///images/pxeboot/initrd.img", }, }, }, copyInputs, )) inputName = "efi-tree" copyInputs = copyPipelineTreeInputs(inputName, "efiboot-tree") p.AddStage(osbuild.NewCopyStageSimple( &osbuild.CopyStageOptions{ Paths: []osbuild.CopyStagePath{ { From: fmt.Sprintf("input://%s/EFI", inputName), To: "tree:///", }, }, }, copyInputs, )) return p, nil } func simplifiedInstallerEFIBootTreePipeline(installDevice, kernelVer, arch, vendor, product, osVersion, isolabel string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "efiboot-tree" p.Build = "name:build" p.AddStage(osbuild.NewGrubISOStage(grubISOStageOptions(installDevice, kernelVer, arch, vendor, product, osVersion, isolabel))) return p } func simplifiedInstallerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "coi-tree" p.Build = "name:build" p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch, product, osVersion, variant))) p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"})) p.AddStage(osbuild.NewDracutStage(dracutStageOptions(kernelVer, arch, []string{"coreos-installer"}))) return p } func ostreeDeployPipeline( t *imageType, pt *disk.PartitionTable, repoPath string, kernel *blueprint.KernelCustomization, kernelVer string, rng *rand.Rand, options distro.ImageOptions, ) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "image-tree" p.Build = "name:build" osname := "redhat" p.AddStage(osbuild.OSTreeInitFsStage()) p.AddStage(osbuild.NewOSTreePullStage( &osbuild.OSTreePullStageOptions{Repo: repoPath}, ostreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref), )) p.AddStage(osbuild.NewOSTreeOsInitStage( &osbuild.OSTreeOsInitStageOptions{ OSName: osname, }, )) p.AddStage(osbuild.NewOSTreeConfigStage(ostreeConfigStageOptions(repoPath, true))) p.AddStage(osbuild.NewMkdirStage(efiMkdirStageOptions())) p.AddStage(osbuild.NewOSTreeDeployStage( &osbuild.OSTreeDeployStageOptions{ OsName: osname, Ref: options.OSTree.Ref, Mounts: []string{"/boot", "/boot/efi"}, Rootfs: osbuild.Rootfs{ Label: "root", }, KernelOpts: []string{ "console=tty0", "console=ttyS0", }, }, )) p.AddStage(osbuild.NewOSTreeFillvarStage( &osbuild.OSTreeFillvarStageOptions{ Deployment: osbuild.OSTreeDeployment{ OSName: osname, Ref: options.OSTree.Ref, }, }, )) fstabOptions := pt.FSTabStageOptionsV2() fstabOptions.OSTree = &osbuild.OSTreeFstab{ Deployment: osbuild.OSTreeDeployment{ OSName: osname, Ref: options.OSTree.Ref, }, } p.AddStage(osbuild.NewFSTabStage(fstabOptions)) // TODO: Add users? p.AddStage(bootloaderConfigStage(t, *pt, kernel, kernelVer, true, true)) p.AddStage(osbuild.NewOSTreeSelinuxStage( &osbuild.OSTreeSelinuxStageOptions{ Deployment: osbuild.OSTreeDeployment{ OSName: osname, Ref: options.OSTree.Ref, }, }, )) return p } func anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "anaconda-tree" p.Build = "name:build" p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(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(anacondaStageOptions())) p.AddStage(osbuild.NewLoraxScriptStage(loraxScriptStageOptions(arch))) p.AddStage(osbuild.NewDracutStage(dracutStageOptions(kernelVer, arch, []string{ "anaconda", "rdma", "rngd", "multipath", "fcoe", "fcoe-uefi", "iscsi", "lunmask", "nfs", }))) 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), bootISOMonoStageInputs())) 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), xorrisofsStageInputs("bootiso-tree"))) p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename})) return p } func liveImagePipeline(inputPipelineName string, outputFilename string, pt *disk.PartitionTable, arch *architecture, kernelVer string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "image" p.Build = "name:build" p.AddStage(osbuild.NewTruncateStage(&osbuild.TruncateStageOptions{Filename: outputFilename, Size: fmt.Sprintf("%d", pt.Size)})) sfOptions := sfdiskStageOptions(pt) loopback := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: outputFilename}) p.AddStage(osbuild.NewSfdiskStage(sfOptions, loopback)) for _, stage := range mkfsStages(pt, loopback) { p.AddStage(stage) } inputName := "root-tree" copyOptions, copyDevices, copyMounts := copyFSTreeOptions(inputName, inputPipelineName, pt, loopback) copyInputs := copyPipelineTreeInputs(inputName, inputPipelineName) p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts)) p.AddStage(bootloaderInstStage(outputFilename, pt, arch, kernelVer, copyDevices, copyMounts, loopback)) return p } func xzArchivePipeline(inputPipelineName, inputFilename, outputFilename string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "archive" p.Build = "name:build" p.AddStage(osbuild.NewXzStage( osbuild.NewXzStageOptions(outputFilename), osbuild.NewFilesInputs(osbuild.NewFilesInputReferencesPipeline(inputPipelineName, inputFilename)), )) return p } // mkfsStages generates a list of org.osbuild.mkfs.* stages based on a // partition table description for a single device node func mkfsStages(pt *disk.PartitionTable, device *osbuild.Device) []*osbuild.Stage { stages := make([]*osbuild.Stage, 0, len(pt.Partitions)) // assume loopback device for simplicity since it's the only one currently supported // panic if the conversion fails devOptions, ok := device.Options.(*osbuild.LoopbackDeviceOptions) if !ok { panic("mkfsStages: failed to convert device options to loopback options") } for _, p := range pt.Partitions { if p.Filesystem == nil { // no filesystem for partition (e.g., BIOS boot) continue } var stage *osbuild.Stage stageDevice := osbuild.NewLoopbackDevice( &osbuild.LoopbackDeviceOptions{ Filename: devOptions.Filename, Start: p.Start, Size: p.Size, }, ) switch p.Filesystem.Type { case "xfs": options := &osbuild.MkfsXfsStageOptions{ UUID: p.Filesystem.UUID, Label: p.Filesystem.Label, } stage = osbuild.NewMkfsXfsStage(options, stageDevice) case "vfat": options := &osbuild.MkfsFATStageOptions{ VolID: strings.Replace(p.Filesystem.UUID, "-", "", -1), } stage = osbuild.NewMkfsFATStage(options, stageDevice) case "btrfs": options := &osbuild.MkfsBtrfsStageOptions{ UUID: p.Filesystem.UUID, Label: p.Filesystem.Label, } stage = osbuild.NewMkfsBtrfsStage(options, stageDevice) case "ext4": options := &osbuild.MkfsExt4StageOptions{ UUID: p.Filesystem.UUID, Label: p.Filesystem.Label, } stage = osbuild.NewMkfsExt4Stage(options, stageDevice) default: panic("unknown fs type " + p.Type) } stages = append(stages, stage) } return stages } func qemuPipeline(inputPipelineName, inputFilename, outputFilename, format, qcow2Compat string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = format p.Build = "name:build" qemuStage := osbuild.NewQEMUStage(qemuStageOptions(outputFilename, format, qcow2Compat), qemuStageInputs(inputPipelineName, inputFilename)) p.AddStage(qemuStage) return p } func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, kernel *blueprint.KernelCustomization, kernelVer string, install, greenboot bool) *osbuild.Stage { if t.arch.name == distro.S390xArchName { return osbuild.NewZiplStage(new(osbuild.ZiplStageOptions)) } kernelOptions := t.kernelOptions uefi := t.supportsUEFI() legacy := t.arch.legacy options := grub2StageOptions(partitionTable.RootPartition(), partitionTable.BootPartition(), kernelOptions, kernel, kernelVer, uefi, legacy, t.arch.distro.vendor, install) options.Greenboot = greenboot return osbuild.NewGRUB2Stage(options) } func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage { platform := arch.legacy if platform != "" { return osbuild.NewGrub2InstStage(grub2InstStageOptions(filename, pt, platform)) } if arch.name == distro.S390xArchName { return osbuild.NewZiplInstStage(ziplInstStageOptions(kernelVer, pt), disk, devices, mounts) } return nil } func kernelVerStr(pkgs []rpmmd.PackageSpec, kernelName, arch string) string { kernelPkg := new(rpmmd.PackageSpec) for _, pkg := range pkgs { if pkg.Name == kernelName { // Implicit memory alasing doesn't couse any bug in this case /* #nosec G601 */ kernelPkg = &pkg break } } if kernelPkg == nil { panic(fmt.Sprintf("kernel package %q not found", kernelName)) } return fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch) }