diff --git a/internal/distro/rhel85/distro.go b/internal/distro/rhel85/distro.go index aa591a992..08a44b6b3 100644 --- a/internal/distro/rhel85/distro.go +++ b/internal/distro/rhel85/distro.go @@ -5,11 +5,9 @@ import ( "errors" "fmt" "math/rand" - "path/filepath" "sort" "github.com/osbuild/osbuild-composer/internal/blueprint" - "github.com/osbuild/osbuild-composer/internal/crypt" "github.com/osbuild/osbuild-composer/internal/distro" osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" "github.com/osbuild/osbuild-composer/internal/rpmmd" @@ -225,79 +223,6 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations, ) } -// checkOptions checks the validity and compatibility of options and customizations for the image type. -func (t *imageType) checkOptions(customizations *blueprint.Customizations, options distro.ImageOptions) error { - if t.bootISO { - if options.OSTree.Parent == "" { - return fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name) - } - if customizations != nil { - return fmt.Errorf("boot ISO image type %q does not support blueprint customizations", t.name) - } - } - - if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree { - return fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") - } - - return nil -} - -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["build"])) - kernelPkg := new(rpmmd.PackageSpec) - installerPackages := packageSetSpecs["installer"] - for _, pkg := range installerPackages { - if pkg.Name == "kernel" { - 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) - pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, options, kernelVer, t.Arch().Name())) - pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, t.Arch().Name())) - pipelines = append(pipelines, *bootISOPipeline(t.Filename(), t.Arch().Name())) - 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["build"])) - - treePipeline, err := ostreeTreePipeline(t, repos, packageSetSpecs["packages"], customizations) - if err != nil { - return nil, err - } - - pipelines = append(pipelines, *treePipeline) - pipelines = append(pipelines, *ostreeCommitPipeline(options)) - - 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 - } - pipelines = append(pipelines, *commitTarPipeline(t.Filename())) - 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 - } - pipelines = append(pipelines, *containerTreePipeline(repos, packageSetSpecs["container"], options, customizations)) - pipelines = append(pipelines, *containerPipeline(t)) - return pipelines, nil -} - func (t *imageType) sources(packages []rpmmd.PackageSpec, ostreeCommits []ostreeCommit) osbuild.Sources { sources := osbuild.Sources{} curl := &osbuild.CurlSource{ @@ -331,559 +256,22 @@ func (t *imageType) sources(packages []rpmmd.PackageSpec, ostreeCommits []ostree return sources } -func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "build" - p.Runner = "org.osbuild.rhel85" - p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(buildPackageSpecs))) - p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) - return p -} - -func ostreeTreePipeline(t *imageType, repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, c *blueprint.Customizations) (*osbuild.Pipeline, error) { - p := new(osbuild.Pipeline) - p.Name = "ostree-tree" - p.Build = "name:build" - p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) - language, keyboard := c.GetPrimaryLocale() - if language != nil { - p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language})) - } else { - p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"})) - } - if keyboard != nil { - p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *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: "America/New_York"})) - } - - if len(ntpServers) > 0 { - p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers})) - } - - if groups := c.GetGroups(); len(groups) > 0 { - p.AddStage(osbuild.NewGroupsStage(groupStageOptions(groups))) - } - - if users := c.GetUsers(); len(users) > 0 { - options, err := userStageOptions(users) - if err != nil { - return nil, err +// checkOptions checks the validity and compatibility of options and customizations for the image type. +func (t *imageType) checkOptions(customizations *blueprint.Customizations, options distro.ImageOptions) error { + if t.bootISO { + if options.OSTree.Parent == "" { + return fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name) } - p.AddStage(osbuild.NewUsersStage(options)) - p.AddStage(osbuild.NewFirstBootStage(usersFirstBootOptions(options))) - } - - if services := c.GetServices(); services != nil || t.enabledServices != nil || t.disabledServices != nil || t.defaultTarget != "" { - p.AddStage(osbuild.NewSystemdStage(systemdStageOptions(t.enabledServices, t.disabledServices, services, t.defaultTarget))) - } - - if firewall := c.GetFirewall(); firewall != nil { - p.AddStage(osbuild.NewFirewallStage(firewallStageOptions(firewall))) - } - - if !t.bootISO { - p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) - } - - // These are the current defaults for the sysconfig stage. This can be changed to be image type exclusive if different configs are needed. - p.AddStage(osbuild.NewSysconfigStage(&osbuild.SysconfigStageOptions{ - Kernel: osbuild.SysconfigKernelOptions{ - UpdateDefault: true, - DefaultKernel: "kernel", - }, - Network: osbuild.SysconfigNetworkOptions{ - Networking: true, - NoZeroConf: true, - }, - })) - - 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) *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 (t *imageType) tarPipeline(reference string) *osbuild.Pipeline { - options := osbuild.TarStageOptions{Filename: t.Filename()} - commitTree := new(osbuild.TarStageInput) - commitTree.Type = "org.osbuild.tree" - commitTree.Origin = "org.osbuild.pipeline" - commitTree.References = []string{fmt.Sprintf("name:$s", reference)} - tarStage := osbuild.NewTarStage(&options, &osbuild.TarStageInputs{Tree: commitTree}) - p := new(osbuild.Pipeline) - p.Name = "archive" - p.Build = "name:build" - p.AddStage(tarStage) - return p -} - -func containerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, c *blueprint.Customizations) *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"})) - } - p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/var/www/html/repo"})) - - p.AddStage(osbuild.NewOSTreePullStage( - &osbuild.OSTreePullStageOptions{Repo: "/var/www/html/repo"}, - ostreePullStageInputs("org.osbuild.pipeline", "name:ostree-commit", options.OSTree.Ref), - )) - return p -} - -func containerPipeline(t *imageType) *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{"httpd", "-D", "FOREGROUND"}, - ExposedPorts: []string{"80"}, - }, - } - 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 anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, kernelVer string, arch string) *osbuild.Pipeline { - ostreeRepoPath := "/ostree/repo" - p := new(osbuild.Pipeline) - p.Name = "anaconda-tree" - p.Build = "name:build" - p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) - p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath})) - p.AddStage(osbuild.NewOSTreePullStage( - &osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath}, - ostreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref), - )) - p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch))) - 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))) - p.AddStage(osbuild.NewKickstartStage(kickstartStageOptions(fmt.Sprintf("file://%s", ostreeRepoPath), options.OSTree.Ref))) - - return p -} - -func bootISOTreePipeline(kernelVer string, arch string) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "bootiso-tree" - p.Build = "name:build" - - p.AddStage(osbuild.NewBootISOMonoStage(bootISOMonoStageOptions(kernelVer, arch), bootISOMonoStageInputs())) - p.AddStage(osbuild.NewDiscinfoStage(discinfoStageOptions(arch))) - - return p -} -func bootISOPipeline(filename string, arch string) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "bootiso" - p.Build = "name:build" - - p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, arch), xorrisofsStageInputs())) - p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename})) - - return p -} - -func rpmStageInputs(specs []rpmmd.PackageSpec) *osbuild.RPMStageInputs { - stageInput := new(osbuild.RPMStageInput) - stageInput.Type = "org.osbuild.files" - stageInput.Origin = "org.osbuild.source" - stageInput.References = pkgRefs(specs) - return &osbuild.RPMStageInputs{Packages: stageInput} -} - -func pkgRefs(specs []rpmmd.PackageSpec) osbuild.RPMStageReferences { - refs := make([]string, len(specs)) - for idx, pkg := range specs { - refs[idx] = pkg.Checksum - } - return refs -} - -func ostreePullStageInputs(origin, source, commitRef string) *osbuild.OSTreePullStageInputs { - pullStageInput := new(osbuild.OSTreePullStageInput) - pullStageInput.Type = "org.osbuild.ostree" - pullStageInput.Origin = origin - - inputRefs := make(map[string]osbuild.OSTreePullStageReference) - inputRefs[source] = osbuild.OSTreePullStageReference{Ref: commitRef} - pullStageInput.References = inputRefs - return &osbuild.OSTreePullStageInputs{Commits: pullStageInput} -} - -func rpmStageOptions(repos []rpmmd.RepoConfig) *osbuild.RPMStageOptions { - var gpgKeys []string - for _, repo := range repos { - if repo.GPGKey == "" { - continue - } - gpgKeys = append(gpgKeys, repo.GPGKey) - } - - return &osbuild.RPMStageOptions{ - GPGKeys: gpgKeys, - Exclude: &osbuild.Exclude{ - // NOTE: Make configurable? - Docs: true, - }, - } -} - -func selinuxStageOptions(bootISO bool) *osbuild.SELinuxStageOptions { - - options := &osbuild.SELinuxStageOptions{ - FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", - } - if bootISO { - options.Labels = map[string]string{ - "/usr/bin/cp": "system_u:object_r:install_exec_t:s0", - "/usr/bin/tar": "system_u:object_r:install_exec_t:s0", + if customizations != nil { + return fmt.Errorf("boot ISO image type %q does not support blueprint customizations", t.name) } } - return options -} -func userStageOptions(users []blueprint.UserCustomization) (*osbuild.UsersStageOptions, error) { - options := osbuild.UsersStageOptions{ - Users: make(map[string]osbuild.UsersStageOptionsUser), + if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree { + return fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") } - for _, c := range users { - if c.Password != nil && !crypt.PasswordIsCrypted(*c.Password) { - cryptedPassword, err := crypt.CryptSHA512(*c.Password) - if err != nil { - return nil, err - } - - c.Password = &cryptedPassword - } - - user := osbuild.UsersStageOptionsUser{ - Groups: c.Groups, - Description: c.Description, - Home: c.Home, - Shell: c.Shell, - Password: c.Password, - Key: c.Key, - } - - user.UID = c.UID - user.GID = c.GID - - options.Users[c.Name] = user - } - - return &options, nil -} - -func usersFirstBootOptions(usersStageOptions *osbuild.UsersStageOptions) *osbuild.FirstBootStageOptions { - cmds := make([]string, 0, 3*len(usersStageOptions.Users)+1) - // workaround for creating authorized_keys file for user - varhome := filepath.Join("/var", "home") - for name, user := range usersStageOptions.Users { - if user.Key != nil { - sshdir := filepath.Join(varhome, name, ".ssh") - cmds = append(cmds, fmt.Sprintf("mkdir -p %s", sshdir)) - cmds = append(cmds, fmt.Sprintf("sh -c 'echo %q >> %q'", *user.Key, filepath.Join(sshdir, "authorized_keys"))) - cmds = append(cmds, fmt.Sprintf("chown %s:%s -Rc %s", name, name, sshdir)) - } - } - cmds = append(cmds, fmt.Sprintf("restorecon -rvF %s", varhome)) - options := &osbuild.FirstBootStageOptions{ - Commands: cmds, - WaitForNetwork: false, - } - - return options -} - -func groupStageOptions(groups []blueprint.GroupCustomization) *osbuild.GroupsStageOptions { - options := osbuild.GroupsStageOptions{ - Groups: map[string]osbuild.GroupsStageOptionsGroup{}, - } - - for _, group := range groups { - groupData := osbuild.GroupsStageOptionsGroup{ - Name: group.Name, - } - groupData.GID = group.GID - - options.Groups[group.Name] = groupData - } - - return &options -} - -func firewallStageOptions(firewall *blueprint.FirewallCustomization) *osbuild.FirewallStageOptions { - options := osbuild.FirewallStageOptions{ - Ports: firewall.Ports, - } - - if firewall.Services != nil { - options.EnabledServices = firewall.Services.Enabled - options.DisabledServices = firewall.Services.Disabled - } - - return &options -} - -func systemdStageOptions(enabledServices, disabledServices []string, s *blueprint.ServicesCustomization, target string) *osbuild.SystemdStageOptions { - if s != nil { - enabledServices = append(enabledServices, s.Enabled...) - disabledServices = append(disabledServices, s.Disabled...) - } - return &osbuild.SystemdStageOptions{ - EnabledServices: enabledServices, - DisabledServices: disabledServices, - DefaultTarget: target, - } -} - -func buildStampStageOptions(arch string) *osbuild.BuildstampStageOptions { - return &osbuild.BuildstampStageOptions{ - Arch: arch, - Product: "Red Hat Enterprise Linux", - Version: osVersion, - Variant: "edge", - Final: true, - } -} - -func anacondaStageOptions() *osbuild.AnacondaStageOptions { - return &osbuild.AnacondaStageOptions{ - KickstartModules: []string{ - "org.fedoraproject.Anaconda.Modules.Network", - "org.fedoraproject.Anaconda.Modules.Payloads", - "org.fedoraproject.Anaconda.Modules.Storage", - }, - } -} - -func loraxScriptStageOptions(arch string) *osbuild.LoraxScriptStageOptions { - return &osbuild.LoraxScriptStageOptions{ - Path: "99-generic/runtime-postinstall.tmpl", - BaseArch: arch, - } -} - -func dracutStageOptions(kernelVer string) *osbuild.DracutStageOptions { - kernel := []string{kernelVer} - modules := []string{ - "bash", - "systemd", - "fips", - "systemd-initrd", - "modsign", - "nss-softokn", - "rdma", - "rngd", - "i18n", - "convertfs", - "network-manager", - "network", - "ifcfg", - "url-lib", - "drm", - "plymouth", - "prefixdevname", - "prefixdevname-tools", - "anaconda", - "crypt", - "dm", - "dmsquash-live", - "kernel-modules", - "kernel-modules-extra", - "kernel-network-modules", - "livenet", - "lvm", - "mdraid", - "multipath", - "qemu", - "qemu-net", - "fcoe", - "fcoe-uefi", - "iscsi", - "lunmask", - "nfs", - "resume", - "rootfs-block", - "terminfo", - "udev-rules", - "biosdevname", - "dracut-systemd", - "pollcdrom", - "usrmount", - "base", - "fs-lib", - "img-lib", - "shutdown", - "uefi-lib", - } - return &osbuild.DracutStageOptions{ - Kernel: kernel, - Modules: modules, - Install: []string{"/.buildstamp"}, - } -} - -func kickstartStageOptions(ostreeURL, ostreeRef string) *osbuild.KickstartStageOptions { - return &osbuild.KickstartStageOptions{ - Path: "/usr/share/anaconda/interactive-defaults.ks", - OSTree: osbuild.OSTreeOptions{ - OSName: "rhel", - URL: ostreeURL, - Ref: ostreeRef, - GPG: false, - }, - } -} - -func bootISOMonoStageOptions(kernelVer string, arch string) *osbuild.BootISOMonoStageOptions { - comprOptions := new(osbuild.FSCompressionOptions) - if bcj := osbuild.BCJOption(arch); bcj != "" { - comprOptions.BCJ = bcj - } - return &osbuild.BootISOMonoStageOptions{ - Product: osbuild.Product{ - Name: "Red Hat Enterprise Linux", - Version: osVersion, - }, - ISOLabel: fmt.Sprintf("RHEL-8-5-0-BaseOS-%s", arch), - Kernel: kernelVer, - EFI: osbuild.EFI{ - Architectures: []string{ - "IA32", - "X64", - }, - Vendor: "redhat", - }, - ISOLinux: osbuild.ISOLinux{ - Enabled: true, - Debug: false, - }, - Templates: "80-rhel", - RootFS: osbuild.RootFS{ - Size: 4096, - Compression: osbuild.FSCompression{ - Method: "xz", - Options: comprOptions, - }, - }, - } -} - -func bootISOMonoStageInputs() *osbuild.BootISOMonoStageInputs { - rootfsInput := new(osbuild.BootISOMonoStageInput) - rootfsInput.Type = "org.osbuild.tree" - rootfsInput.Origin = "org.osbuild.pipeline" - rootfsInput.References = osbuild.BootISOMonoStageReferences{"name:anaconda-tree"} - return &osbuild.BootISOMonoStageInputs{ - RootFS: rootfsInput, - } -} - -func discinfoStageOptions(arch string) *osbuild.DiscinfoStageOptions { - return &osbuild.DiscinfoStageOptions{ - BaseArch: arch, - Release: "202010217.n.0", - } -} - -func xorrisofsStageOptions(filename string, arch string) *osbuild.XorrisofsStageOptions { - return &osbuild.XorrisofsStageOptions{ - Filename: filename, - VolID: fmt.Sprintf("RHEL-8-5-0-BaseOS-%s", arch), - SysID: "LINUX", - Boot: osbuild.XorrisofsBoot{ - Image: "isolinux/isolinux.bin", - Catalog: "isolinux/boot.cat", - }, - EFI: "images/efiboot.img", - IsohybridMBR: "/usr/share/syslinux/isohdpfx.bin", - } -} - -func xorrisofsStageInputs() *osbuild.XorrisofsStageInputs { - input := new(osbuild.XorrisofsStageInput) - input.Type = "org.osbuild.tree" - input.Origin = "org.osbuild.pipeline" - input.References = osbuild.XorrisofsStageReferences{"name:bootiso-tree"} - return &osbuild.XorrisofsStageInputs{Tree: input} + return nil } // New creates a new distro object, defining the supported architectures and image types diff --git a/internal/distro/rhel85/pipelines.go b/internal/distro/rhel85/pipelines.go new file mode 100644 index 000000000..91a171217 --- /dev/null +++ b/internal/distro/rhel85/pipelines.go @@ -0,0 +1,291 @@ +package rhel85 + +import ( + "fmt" + "math/rand" + + "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/distro" + osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + +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["build"])) + kernelPkg := new(rpmmd.PackageSpec) + installerPackages := packageSetSpecs["installer"] + for _, pkg := range installerPackages { + if pkg.Name == "kernel" { + 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) + pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, options, kernelVer, t.Arch().Name())) + pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, t.Arch().Name())) + pipelines = append(pipelines, *bootISOPipeline(t.Filename(), t.Arch().Name())) + 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["build"])) + + treePipeline, err := ostreeTreePipeline(repos, packageSetSpecs["packages"], customizations, t.enabledServices, t.disabledServices, t.defaultTarget) + if err != nil { + return nil, err + } + + pipelines = append(pipelines, *treePipeline) + pipelines = append(pipelines, *ostreeCommitPipeline(options)) + + 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 + } + pipelines = append(pipelines, *commitTarPipeline(t.Filename())) + 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 + } + pipelines = append(pipelines, *containerTreePipeline(repos, packageSetSpecs["container"], options, customizations)) + pipelines = append(pipelines, *containerPipeline(t)) + return pipelines, nil +} + +func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec) *osbuild.Pipeline { + p := new(osbuild.Pipeline) + p.Name = "build" + p.Runner = "org.osbuild.rhel85" + p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(buildPackageSpecs))) + p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) + return p +} + +func ostreeTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, c *blueprint.Customizations, enabledServices, disabledServices []string, defaultTarget string) (*osbuild.Pipeline, error) { + p := new(osbuild.Pipeline) + p.Name = "ostree-tree" + p.Build = "name:build" + p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) + language, keyboard := c.GetPrimaryLocale() + if language != nil { + p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language})) + } else { + p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"})) + } + if keyboard != nil { + p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *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: "America/New_York"})) + } + + if len(ntpServers) > 0 { + p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers})) + } + + if groups := c.GetGroups(); len(groups) > 0 { + p.AddStage(osbuild.NewGroupsStage(groupStageOptions(groups))) + } + + if users := c.GetUsers(); len(users) > 0 { + options, err := userStageOptions(users) + if err != nil { + return nil, err + } + p.AddStage(osbuild.NewUsersStage(options)) + p.AddStage(osbuild.NewFirstBootStage(usersFirstBootOptions(options))) + } + + if services := c.GetServices(); services != nil || enabledServices != nil || disabledServices != nil || defaultTarget != "" { + p.AddStage(osbuild.NewSystemdStage(systemdStageOptions(enabledServices, disabledServices, services, defaultTarget))) + } + + if firewall := c.GetFirewall(); firewall != nil { + p.AddStage(osbuild.NewFirewallStage(firewallStageOptions(firewall))) + } + p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false))) + + // These are the current defaults for the sysconfig stage. This can be changed to be image type exclusive if different configs are needed. + p.AddStage(osbuild.NewSysconfigStage(&osbuild.SysconfigStageOptions{ + Kernel: osbuild.SysconfigKernelOptions{ + UpdateDefault: true, + DefaultKernel: "kernel", + }, + Network: osbuild.SysconfigNetworkOptions{ + Networking: true, + NoZeroConf: true, + }, + })) + + 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) *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 commitTarPipeline(filename string) *osbuild.Pipeline { + options := osbuild.TarStageOptions{Filename: filename} + commitTree := new(osbuild.TarStageInput) + commitTree.Type = "org.osbuild.tree" + commitTree.Origin = "org.osbuild.pipeline" + commitTree.References = []string{"name:ostree-commit"} + tarStage := osbuild.NewTarStage(&options, &osbuild.TarStageInputs{Tree: commitTree}) + p := new(osbuild.Pipeline) + p.Name = "commit-archive" + p.Build = "name:build" + p.AddStage(tarStage) + return p +} + +func containerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, c *blueprint.Customizations) *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"})) + } + p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/var/www/html/repo"})) + + p.AddStage(osbuild.NewOSTreePullStage( + &osbuild.OSTreePullStageOptions{Repo: "/var/www/html/repo"}, + ostreePullStageInputs("org.osbuild.pipeline", "name:ostree-commit", options.OSTree.Ref), + )) + return p +} + +func containerPipeline(t *imageType) *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{"httpd", "-D", "FOREGROUND"}, + ExposedPorts: []string{"80"}, + }, + } + 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 anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, kernelVer string, arch string) *osbuild.Pipeline { + ostreeRepoPath := "/ostree/repo" + p := new(osbuild.Pipeline) + p.Name = "anaconda-tree" + p.Build = "name:build" + p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages))) + p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath})) + p.AddStage(osbuild.NewOSTreePullStage( + &osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath}, + ostreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref), + )) + p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch))) + 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))) + p.AddStage(osbuild.NewKickstartStage(kickstartStageOptions(fmt.Sprintf("file://%s", ostreeRepoPath), options.OSTree.Ref))) + + return p +} + +func bootISOTreePipeline(kernelVer string, arch string) *osbuild.Pipeline { + p := new(osbuild.Pipeline) + p.Name = "bootiso-tree" + p.Build = "name:build" + + p.AddStage(osbuild.NewBootISOMonoStage(bootISOMonoStageOptions(kernelVer, arch), bootISOMonoStageInputs())) + p.AddStage(osbuild.NewDiscinfoStage(discinfoStageOptions(arch))) + + return p +} +func bootISOPipeline(filename string, arch string) *osbuild.Pipeline { + p := new(osbuild.Pipeline) + p.Name = "bootiso" + p.Build = "name:build" + + p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, arch), xorrisofsStageInputs())) + p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename})) + + return p +} diff --git a/internal/distro/rhel85/stage_inputs.go b/internal/distro/rhel85/stage_inputs.go new file mode 100644 index 000000000..8ef119ead --- /dev/null +++ b/internal/distro/rhel85/stage_inputs.go @@ -0,0 +1,51 @@ +package rhel85 + +import ( + osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + +func bootISOMonoStageInputs() *osbuild.BootISOMonoStageInputs { + rootfsInput := new(osbuild.BootISOMonoStageInput) + rootfsInput.Type = "org.osbuild.tree" + rootfsInput.Origin = "org.osbuild.pipeline" + rootfsInput.References = osbuild.BootISOMonoStageReferences{"name:anaconda-tree"} + return &osbuild.BootISOMonoStageInputs{ + RootFS: rootfsInput, + } +} + +func rpmStageInputs(specs []rpmmd.PackageSpec) *osbuild.RPMStageInputs { + stageInput := new(osbuild.RPMStageInput) + stageInput.Type = "org.osbuild.files" + stageInput.Origin = "org.osbuild.source" + stageInput.References = pkgRefs(specs) + return &osbuild.RPMStageInputs{Packages: stageInput} +} + +func pkgRefs(specs []rpmmd.PackageSpec) osbuild.RPMStageReferences { + refs := make([]string, len(specs)) + for idx, pkg := range specs { + refs[idx] = pkg.Checksum + } + return refs +} + +func ostreePullStageInputs(origin, source, commitRef string) *osbuild.OSTreePullStageInputs { + pullStageInput := new(osbuild.OSTreePullStageInput) + pullStageInput.Type = "org.osbuild.ostree" + pullStageInput.Origin = origin + + inputRefs := make(map[string]osbuild.OSTreePullStageReference) + inputRefs[source] = osbuild.OSTreePullStageReference{Ref: commitRef} + pullStageInput.References = inputRefs + return &osbuild.OSTreePullStageInputs{Commits: pullStageInput} +} + +func xorrisofsStageInputs() *osbuild.XorrisofsStageInputs { + input := new(osbuild.XorrisofsStageInput) + input.Type = "org.osbuild.tree" + input.Origin = "org.osbuild.pipeline" + input.References = osbuild.XorrisofsStageReferences{"name:bootiso-tree"} + return &osbuild.XorrisofsStageInputs{Tree: input} +} diff --git a/internal/distro/rhel85/stage_options.go b/internal/distro/rhel85/stage_options.go new file mode 100644 index 000000000..4ffb7cf02 --- /dev/null +++ b/internal/distro/rhel85/stage_options.go @@ -0,0 +1,293 @@ +package rhel85 + +import ( + "fmt" + "path/filepath" + + "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/crypt" + osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + +func rpmStageOptions(repos []rpmmd.RepoConfig) *osbuild.RPMStageOptions { + var gpgKeys []string + for _, repo := range repos { + if repo.GPGKey == "" { + continue + } + gpgKeys = append(gpgKeys, repo.GPGKey) + } + + return &osbuild.RPMStageOptions{ + GPGKeys: gpgKeys, + Exclude: &osbuild.Exclude{ + // NOTE: Make configurable? + Docs: true, + }, + } +} + +func selinuxStageOptions(bootISO bool) *osbuild.SELinuxStageOptions { + + options := &osbuild.SELinuxStageOptions{ + FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", + } + if bootISO { + options.Labels = map[string]string{ + "/usr/bin/cp": "system_u:object_r:install_exec_t:s0", + "/usr/bin/tar": "system_u:object_r:install_exec_t:s0", + } + } + return options +} + +func userStageOptions(users []blueprint.UserCustomization) (*osbuild.UsersStageOptions, error) { + options := osbuild.UsersStageOptions{ + Users: make(map[string]osbuild.UsersStageOptionsUser), + } + + for _, c := range users { + if c.Password != nil && !crypt.PasswordIsCrypted(*c.Password) { + cryptedPassword, err := crypt.CryptSHA512(*c.Password) + if err != nil { + return nil, err + } + + c.Password = &cryptedPassword + } + + user := osbuild.UsersStageOptionsUser{ + Groups: c.Groups, + Description: c.Description, + Home: c.Home, + Shell: c.Shell, + Password: c.Password, + Key: c.Key, + } + + user.UID = c.UID + user.GID = c.GID + + options.Users[c.Name] = user + } + + return &options, nil +} + +func usersFirstBootOptions(usersStageOptions *osbuild.UsersStageOptions) *osbuild.FirstBootStageOptions { + cmds := make([]string, 0, 3*len(usersStageOptions.Users)+1) + // workaround for creating authorized_keys file for user + varhome := filepath.Join("/var", "home") + for name, user := range usersStageOptions.Users { + if user.Key != nil { + sshdir := filepath.Join(varhome, name, ".ssh") + cmds = append(cmds, fmt.Sprintf("mkdir -p %s", sshdir)) + cmds = append(cmds, fmt.Sprintf("sh -c 'echo %q >> %q'", *user.Key, filepath.Join(sshdir, "authorized_keys"))) + cmds = append(cmds, fmt.Sprintf("chown %s:%s -Rc %s", name, name, sshdir)) + } + } + cmds = append(cmds, fmt.Sprintf("restorecon -rvF %s", varhome)) + options := &osbuild.FirstBootStageOptions{ + Commands: cmds, + WaitForNetwork: false, + } + + return options +} + +func groupStageOptions(groups []blueprint.GroupCustomization) *osbuild.GroupsStageOptions { + options := osbuild.GroupsStageOptions{ + Groups: map[string]osbuild.GroupsStageOptionsGroup{}, + } + + for _, group := range groups { + groupData := osbuild.GroupsStageOptionsGroup{ + Name: group.Name, + } + groupData.GID = group.GID + + options.Groups[group.Name] = groupData + } + + return &options +} + +func firewallStageOptions(firewall *blueprint.FirewallCustomization) *osbuild.FirewallStageOptions { + options := osbuild.FirewallStageOptions{ + Ports: firewall.Ports, + } + + if firewall.Services != nil { + options.EnabledServices = firewall.Services.Enabled + options.DisabledServices = firewall.Services.Disabled + } + + return &options +} + +func systemdStageOptions(enabledServices, disabledServices []string, s *blueprint.ServicesCustomization, target string) *osbuild.SystemdStageOptions { + if s != nil { + enabledServices = append(enabledServices, s.Enabled...) + disabledServices = append(disabledServices, s.Disabled...) + } + return &osbuild.SystemdStageOptions{ + EnabledServices: enabledServices, + DisabledServices: disabledServices, + DefaultTarget: target, + } +} + +func buildStampStageOptions(arch string) *osbuild.BuildstampStageOptions { + return &osbuild.BuildstampStageOptions{ + Arch: arch, + Product: "Red Hat Enterprise Linux", + Version: osVersion, + Variant: "edge", + Final: true, + } +} + +func anacondaStageOptions() *osbuild.AnacondaStageOptions { + return &osbuild.AnacondaStageOptions{ + KickstartModules: []string{ + "org.fedoraproject.Anaconda.Modules.Network", + "org.fedoraproject.Anaconda.Modules.Payloads", + "org.fedoraproject.Anaconda.Modules.Storage", + }, + } +} + +func loraxScriptStageOptions(arch string) *osbuild.LoraxScriptStageOptions { + return &osbuild.LoraxScriptStageOptions{ + Path: "99-generic/runtime-postinstall.tmpl", + BaseArch: arch, + } +} + +func dracutStageOptions(kernelVer string) *osbuild.DracutStageOptions { + kernel := []string{kernelVer} + modules := []string{ + "bash", + "systemd", + "fips", + "systemd-initrd", + "modsign", + "nss-softokn", + "rdma", + "rngd", + "i18n", + "convertfs", + "network-manager", + "network", + "ifcfg", + "url-lib", + "drm", + "plymouth", + "prefixdevname", + "prefixdevname-tools", + "anaconda", + "crypt", + "dm", + "dmsquash-live", + "kernel-modules", + "kernel-modules-extra", + "kernel-network-modules", + "livenet", + "lvm", + "mdraid", + "multipath", + "qemu", + "qemu-net", + "fcoe", + "fcoe-uefi", + "iscsi", + "lunmask", + "nfs", + "resume", + "rootfs-block", + "terminfo", + "udev-rules", + "biosdevname", + "dracut-systemd", + "pollcdrom", + "usrmount", + "base", + "fs-lib", + "img-lib", + "shutdown", + "uefi-lib", + } + return &osbuild.DracutStageOptions{ + Kernel: kernel, + Modules: modules, + Install: []string{"/.buildstamp"}, + } +} + +func kickstartStageOptions(ostreeURL, ostreeRef string) *osbuild.KickstartStageOptions { + return &osbuild.KickstartStageOptions{ + Path: "/usr/share/anaconda/interactive-defaults.ks", + OSTree: osbuild.OSTreeOptions{ + OSName: "rhel", + URL: ostreeURL, + Ref: ostreeRef, + GPG: false, + }, + } +} + +func bootISOMonoStageOptions(kernelVer string, arch string) *osbuild.BootISOMonoStageOptions { + comprOptions := new(osbuild.FSCompressionOptions) + if bcj := osbuild.BCJOption(arch); bcj != "" { + comprOptions.BCJ = bcj + } + return &osbuild.BootISOMonoStageOptions{ + Product: osbuild.Product{ + Name: "Red Hat Enterprise Linux", + Version: osVersion, + }, + ISOLabel: fmt.Sprintf("RHEL-8-5-0-BaseOS-%s", arch), + Kernel: kernelVer, + EFI: osbuild.EFI{ + Architectures: []string{ + "IA32", + "X64", + }, + Vendor: "redhat", + }, + ISOLinux: osbuild.ISOLinux{ + Enabled: true, + Debug: false, + }, + Templates: "80-rhel", + RootFS: osbuild.RootFS{ + Size: 4096, + 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 string, arch string) *osbuild.XorrisofsStageOptions { + return &osbuild.XorrisofsStageOptions{ + Filename: filename, + VolID: fmt.Sprintf("RHEL-8-5-0-BaseOS-%s", arch), + SysID: "LINUX", + Boot: osbuild.XorrisofsBoot{ + Image: "isolinux/isolinux.bin", + Catalog: "isolinux/boot.cat", + }, + EFI: "images/efiboot.img", + IsohybridMBR: "/usr/share/syslinux/isohdpfx.bin", + } +}