debian-forge-composer/internal/distro/rhel84/distro_v2.go
Tomas Hozza fee529cd0a distro: introduce PackageSetsChains() method to ImageType
Introduce a new method `PackageSetsChains()` to the `ImageType`
interface, which returns a named lists of package sets, which should be
depolved together in a chain.

Extend all distro implementations with the new method.

Add a unit test ensuring that if an image type defines some package set
name chains, that all of the listed package set names are present in the
package set map returned by the image type.

The method is currently not used anywhere. This is a preparation for
switching from current way of depsolving to the chain depsolving.
2022-04-28 14:42:49 +02:00

762 lines
24 KiB
Go

package rhel84
import (
"encoding/json"
"fmt"
"math/rand"
"path/filepath"
"strings"
"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/blueprint"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
const (
kspath = "/usr/share/anaconda/interactive-defaults.ks"
)
type pipelinesFunc func(t *imageTypeS2, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error)
type imageTypeS2 struct {
arch *architecture
name string
filename string
mimeType string
packageSets map[string]rpmmd.PackageSet
enabledServices []string
disabledServices []string
defaultTarget string
kernelOptions string
bootable bool
bootISO bool
rpmOstree bool
defaultSize uint64
buildPipelines []string
payloadPipelines []string
exports []string
partitionTableGenerator func(imageSize uint64, arch distro.Arch, rng *rand.Rand) disk.PartitionTable
pipelines pipelinesFunc
}
func (t *imageTypeS2) Arch() distro.Arch {
return t.arch
}
func (t *imageTypeS2) Name() string {
return t.name
}
func (t *imageTypeS2) Filename() string {
return t.filename
}
func (t *imageTypeS2) MIMEType() string {
return t.mimeType
}
func (t *imageTypeS2) OSTreeRef() string {
if t.rpmOstree {
return fmt.Sprintf(ostreeRef, t.arch.name)
}
return ""
}
func (t *imageTypeS2) Size(size uint64) uint64 {
const MegaByte = 1024 * 1024
// Microsoft Azure requires vhd images to be rounded up to the nearest MB
if t.name == "vhd" && size%MegaByte != 0 {
size = (size/MegaByte + 1) * MegaByte
}
if size == 0 {
size = t.defaultSize
}
return size
}
func (t *imageTypeS2) PartitionType() string {
return ""
}
func (t *imageTypeS2) Packages(bp blueprint.Blueprint) ([]string, []string) {
packages := append(t.packageSets["packages"].Include, bp.GetPackages()...)
timezone, _ := bp.Customizations.GetTimezoneSettings()
if timezone != nil {
packages = append(packages, "chrony")
}
// copy the list of excluded packages from the image type
// and subtract any packages found in the blueprint (this
// will not handle the issue with dependencies present in
// the list of excluded packages, but it will create a
// possibility of a workaround at least)
excludedPackages := append([]string(nil), t.packageSets["packages"].Exclude...)
for _, pkg := range bp.GetPackages() {
// removePackage is fine if the package doesn't exist
excludedPackages = removePackage(excludedPackages, pkg)
}
return packages, excludedPackages
}
func (t *imageTypeS2) BuildPackages() []string {
buildPackages := append(t.arch.distro.buildPackages, t.arch.buildPackages...)
if t.rpmOstree {
buildPackages = append(buildPackages, "rpm-ostree")
}
if t.bootISO {
buildPackages = append(buildPackages, t.packageSets["build"].Include...)
}
return buildPackages
}
func (t *imageTypeS2) PackageSets(bp blueprint.Blueprint) map[string]rpmmd.PackageSet {
sets := map[string]rpmmd.PackageSet{
"build-packages": {
Include: t.BuildPackages(),
},
}
for name, pkgSet := range t.packageSets {
if name == "packages" {
// treat base packages separately to combine with blueprint
packages := new(rpmmd.PackageSet)
packages.Include, packages.Exclude = t.Packages(bp)
sets[name] = *packages
continue
}
sets[name] = pkgSet
}
return sets
}
func (t *imageTypeS2) BuildPipelines() []string {
return t.buildPipelines
}
func (t *imageTypeS2) PayloadPipelines() []string {
return t.payloadPipelines
}
func (t *imageTypeS2) PayloadPackageSets() []string {
return []string{"packages"}
}
func (t *imageTypeS2) PackageSetsChains() map[string][]string {
return map[string][]string{}
}
func (t *imageTypeS2) Exports() []string {
return t.exports
}
func (t *imageTypeS2) Manifest(c *blueprint.Customizations,
options distro.ImageOptions,
repos []rpmmd.RepoConfig,
packageSpecSets map[string][]rpmmd.PackageSpec,
seed int64) (distro.Manifest, error) {
if err := t.checkOptions(c, options); err != nil {
return distro.Manifest{}, err
}
source := rand.NewSource(seed)
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(source)
pipelines, err := t.pipelines(t, c, options, repos, packageSpecSets, rng)
if err != nil {
return distro.Manifest{}, err
}
// flatten spec sets for sources
allPackageSpecs := make([]rpmmd.PackageSpec, 0)
for _, specs := range packageSpecSets {
allPackageSpecs = append(allPackageSpecs, specs...)
}
var commits []ostreeCommit
if t.bootISO && options.OSTree.Parent != "" && options.OSTree.URL != "" {
commit := ostreeCommit{Checksum: options.OSTree.Parent, URL: options.OSTree.URL}
commits = []ostreeCommit{commit}
}
return json.Marshal(
osbuild.Manifest{
Version: "2",
Pipelines: pipelines,
Sources: t.sources(allPackageSpecs, commits),
},
)
}
// local type for ostree commit metadata used to define commit sources
type ostreeCommit struct {
Checksum string
URL string
}
func (t *imageTypeS2) sources(packages []rpmmd.PackageSpec, ostreeCommits []ostreeCommit) osbuild.Sources {
sources := osbuild.Sources{}
curl := &osbuild.CurlSource{
Items: make(map[string]osbuild.CurlSourceItem),
}
for _, pkg := range packages {
item := new(osbuild.URLWithSecrets)
item.URL = pkg.RemoteLocation
if pkg.Secrets == "org.osbuild.rhsm" {
item.Secrets = &osbuild.URLSecrets{
Name: "org.osbuild.rhsm",
}
}
curl.Items[pkg.Checksum] = item
}
if len(curl.Items) > 0 {
sources["org.osbuild.curl"] = curl
}
ostree := &osbuild.OSTreeSource{
Items: make(map[string]osbuild.OSTreeSourceItem),
}
for _, commit := range ostreeCommits {
item := new(osbuild.OSTreeSourceItem)
item.Remote.URL = commit.URL
ostree.Items[commit.Checksum] = *item
}
if len(ostree.Items) > 0 {
sources["org.osbuild.ostree"] = ostree
}
return sources
}
func edgePipelines(t *imageTypeS2, 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, *t.buildPipeline(repos, packageSetSpecs["build-packages"]))
if t.bootISO {
var kernelPkg *rpmmd.PackageSpec
installerPackages := packageSetSpecs["installer"]
for idx := range installerPackages {
pkg := installerPackages[idx]
if pkg.Name == "kernel" {
kernelPkg = &pkg
break
}
}
if kernelPkg == nil {
panic("kernel package not found in installer package set; this is a programming error")
}
kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch)
anacondaPipeline, err := t.anacondaTreePipeline(repos, customizations, packageSetSpecs["installer"], options, kernelVer)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *anacondaPipeline)
pipelines = append(pipelines, *t.bootISOTreePipeline(kernelVer))
pipelines = append(pipelines, *t.bootISOPipeline())
} else {
treePipeline, err := t.ostreeTreePipeline(repos, packageSetSpecs["packages"], customizations)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, *t.ostreeCommitPipeline(options))
pipelines = append(pipelines, *t.containerTreePipeline(repos, packageSetSpecs["container"], options, customizations))
pipelines = append(pipelines, *t.containerPipeline())
}
return pipelines, nil
}
func (t *imageTypeS2) buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec) *osbuild.Pipeline {
p := new(osbuild.Pipeline)
p.Name = "build"
p.Runner = "org.osbuild.rhel84"
p.AddStage(osbuild.NewRPMStage(t.rpmStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(buildPackageSpecs)))
p.AddStage(osbuild.NewSELinuxStage(t.selinuxStageOptions()))
return p
}
func (t *imageTypeS2) ostreeTreePipeline(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(t.rpmStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(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(osbuild.NewGroupsStageOptions(groups)))
}
if userOptions, err := osbuild.NewUsersStageOptions(c.GetUsers(), false); err != nil {
return nil, err
} else if userOptions != nil {
// for ostree, writing the key during user creation is redundant and
// can cause issues so create users without keys and write them on
// first boot
userOptionsSansKeys, err := osbuild.NewUsersStageOptions(c.GetUsers(), true)
if err != nil {
return nil, err
}
p.AddStage(osbuild.NewUsersStage(userOptionsSansKeys))
p.AddStage(osbuild.NewFirstBootStage(t.usersFirstBootOptions(userOptions)))
}
if services := c.GetServices(); services != nil || t.enabledServices != nil || t.disabledServices != nil || t.defaultTarget != "" {
p.AddStage(osbuild.NewSystemdStage(t.systemdStageOptions(t.enabledServices, t.disabledServices, services, t.defaultTarget)))
}
if firewall := c.GetFirewall(); firewall != nil {
p.AddStage(osbuild.NewFirewallStage(t.firewallStageOptions(firewall)))
}
if !t.bootISO {
p.AddStage(osbuild.NewSELinuxStage(t.selinuxStageOptions()))
}
// 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 (t *imageTypeS2) 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: "8.4", // NOTE: Set on image type?
Parent: options.OSTree.Parent,
},
&osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}),
)
return p
}
func (t *imageTypeS2) 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(t.rpmStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(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"},
osbuild.NewOstreePullStageInputs("org.osbuild.pipeline", "name:ostree-commit", options.OSTree.Ref),
))
return p
}
func (t *imageTypeS2) containerPipeline() *osbuild.Pipeline {
p := new(osbuild.Pipeline)
// NOTE(akoutsou) 1to2t: final pipeline should always be named "assembler"
p.Name = "assembler"
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 (t *imageTypeS2) anacondaTreePipeline(repos []rpmmd.RepoConfig, customizations *blueprint.Customizations, packages []rpmmd.PackageSpec, options distro.ImageOptions, kernelVer string) (*osbuild.Pipeline, error) {
ostreeRepoPath := "/ostree/repo"
p := new(osbuild.Pipeline)
p.Name = "anaconda-tree"
p.Build = "name:build"
p.AddStage(osbuild.NewRPMStage(t.rpmStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(packages)))
p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath}))
p.AddStage(osbuild.NewOSTreePullStage(
&osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath},
osbuild.NewOstreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref),
))
p.AddStage(osbuild.NewBuildstampStage(t.buildStampStageOptions()))
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))
nUsers := len(customizations.GetUsers())+len(customizations.GetGroups()) > 0
p.AddStage(osbuild.NewAnacondaStage(osbuild.NewAnacondaStageOptions(nUsers)))
p.AddStage(osbuild.NewLoraxScriptStage(t.loraxScriptStageOptions()))
p.AddStage(osbuild.NewDracutStage(t.dracutStageOptions(kernelVer)))
kickstartOptions, err := osbuild.NewKickstartStageOptions(kspath, "", customizations.GetUsers(), customizations.GetGroups(), fmt.Sprintf("file://%s", ostreeRepoPath), options.OSTree.Ref)
if err != nil {
return nil, err
}
p.AddStage(osbuild.NewKickstartStage(kickstartOptions))
return p, nil
}
func (t *imageTypeS2) bootISOTreePipeline(kernelVer string) *osbuild.Pipeline {
p := new(osbuild.Pipeline)
p.Name = "bootiso-tree"
p.Build = "name:build"
p.AddStage(osbuild.NewBootISOMonoStage(t.bootISOMonoStageOptions(kernelVer), osbuild.NewBootISOMonoStagePipelineTreeInputs("anaconda-tree")))
p.AddStage(osbuild.NewDiscinfoStage(t.discinfoStageOptions()))
return p
}
func (t *imageTypeS2) bootISOPipeline() *osbuild.Pipeline {
p := new(osbuild.Pipeline)
// NOTE(akoutsou) 1to2t: final pipeline should always be named "assembler"
p.Name = "assembler"
p.Build = "name:build"
p.AddStage(osbuild.NewXorrisofsStage(t.xorrisofsStageOptions(), osbuild.NewXorrisofsStagePipelineTreeInputs("bootiso-tree")))
p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: t.Filename()}))
return p
}
func (t *imageTypeS2) 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 (t *imageTypeS2) selinuxStageOptions() *osbuild.SELinuxStageOptions {
options := &osbuild.SELinuxStageOptions{
FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
}
if t.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 (t *imageTypeS2) 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 (t *imageTypeS2) 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 (t *imageTypeS2) 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 (t *imageTypeS2) buildStampStageOptions() *osbuild.BuildstampStageOptions {
return &osbuild.BuildstampStageOptions{
Arch: t.Arch().Name(),
Product: "Red Hat Enterprise Linux",
Version: "8.4",
Variant: "edge",
Final: true,
}
}
func (t *imageTypeS2) loraxScriptStageOptions() *osbuild.LoraxScriptStageOptions {
return &osbuild.LoraxScriptStageOptions{
Path: "99-generic/runtime-postinstall.tmpl",
BaseArch: t.Arch().Name(),
}
}
func (t *imageTypeS2) 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 (t *imageTypeS2) bootISOMonoStageOptions(kernelVer string) *osbuild.BootISOMonoStageOptions {
return &osbuild.BootISOMonoStageOptions{
Product: osbuild.Product{
Name: "Red Hat Enterprise Linux",
Version: "8.4",
},
ISOLabel: fmt.Sprintf("RHEL-8-4-0-BaseOS-%s", t.Arch().Name()),
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: &osbuild.FSCompressionOptions{
// TODO: based on image arch
BCJ: "x86",
},
},
},
}
}
func (t *imageTypeS2) discinfoStageOptions() *osbuild.DiscinfoStageOptions {
return &osbuild.DiscinfoStageOptions{
BaseArch: t.Arch().Name(),
Release: "202010217.n.0",
}
}
func (t *imageTypeS2) xorrisofsStageOptions() *osbuild.XorrisofsStageOptions {
return &osbuild.XorrisofsStageOptions{
Filename: t.Filename(),
VolID: fmt.Sprintf("RHEL-8-4-0-BaseOS-%s", t.Arch().Name()),
SysID: "LINUX",
Boot: &osbuild.XorrisofsBoot{
Image: "isolinux/isolinux.bin",
Catalog: "isolinux/boot.cat",
},
EFI: "images/efiboot.img",
IsohybridMBR: "/usr/share/syslinux/isohdpfx.bin",
}
}
func (t *imageTypeS2) 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 t.name == "rhel-edge-installer" {
allowed := []string{"User", "Group"}
if err := customizations.CheckAllowed(allowed...); err != nil {
return fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
}
}
}
if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree {
return fmt.Errorf("kernel boot parameter customizations are not supported for ostree types")
}
mountpoints := customizations.GetFilesystems()
if mountpoints != nil && t.rpmOstree {
return fmt.Errorf("Custom mountpoints are not supported for ostree types")
}
invalidMountpoints := []string{}
for _, m := range mountpoints {
if m.Mountpoint != "/" {
invalidMountpoints = append(invalidMountpoints, m.Mountpoint)
}
}
if len(invalidMountpoints) > 0 {
return fmt.Errorf("The following custom mountpoints are not supported %+q", invalidMountpoints)
}
return nil
}
func (t *imageTypeS2) prependKernelCmdlineStage(pipeline *osbuild.Pipeline, pt *disk.PartitionTable) *osbuild.Pipeline {
if t.arch.name == distro.S390xArchName {
rootFs := pt.FindMountable("/")
if rootFs == nil {
panic("s390x image must have a root partition, this is a programming error")
}
kernelStage := osbuild.NewKernelCmdlineStage(osbuild.NewKernelCmdlineStageOptions(rootFs.GetFSSpec().UUID, t.kernelOptions))
pipeline.Stages = append([]*osbuild.Stage{kernelStage}, pipeline.Stages...)
}
return pipeline
}
func (t *imageTypeS2) getPartitionTable(options distro.ImageOptions, rng *rand.Rand) (*disk.PartitionTable, error) {
basePartitionTable := t.partitionTableGenerator(options.Size, t.Arch(), rng)
pt, err := disk.NewPartitionTable(&basePartitionTable, nil, options.Size, false, rng)
return pt, err
}