debian-forge-composer/internal/distro/fedora/distro.go
Tom Gundersen 4961a17ba8 manifest: implicitly track pipelines in manifest
Pipelines are now added to their manifest on creation, and we
ensure that dependants are associated with the same manifest.
2022-07-04 23:04:29 +01:00

921 lines
25 KiB
Go

package fedora
import (
"errors"
"fmt"
"math/rand"
"path"
"sort"
"strings"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/disk"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/manifest"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
const (
GigaByte = 1024 * 1024 * 1024
// package set names
// build package set name
buildPkgsKey = "build"
// main/common os image package set name
osPkgsKey = "packages"
// container package set name
containerPkgsKey = "container"
// installer package set name
installerPkgsKey = "installer"
// blueprint package set name
blueprintPkgsKey = "blueprint"
// Fedora distribution
fedora34Distribution = "fedora-34"
fedora35Distribution = "fedora-35"
fedora36Distribution = "fedora-36"
//Kernel options for ami, qcow2, openstack, vhd and vmdk types
defaultKernelOptions = "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0"
)
var (
mountpointAllowList = []string{
"/", "/var", "/opt", "/srv", "/usr", "/app", "/data", "/home", "/tmp",
}
// Services
iotServices = []string{
"NetworkManager.service",
"firewalld.service",
"rngd.service",
"sshd.service",
"zezere_ignition.timer",
"zezere_ignition_banner.service",
"greenboot-grub2-set-counter",
"greenboot-grub2-set-success",
"greenboot-healthcheck",
"greenboot-rpm-ostree-grub2-check-fallback",
"greenboot-status",
"greenboot-task-runner",
"redboot-auto-reboot",
"redboot-task-runner",
"parsec",
"dbus-parsec",
}
// Image Definitions
iotCommitImgType = imageType{
name: "fedora-iot-commit",
nameAliases: []string{"iot-commit"},
filename: "commit.tar",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
buildPkgsKey: iotBuildPackageSet,
osPkgsKey: iotCommitPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
EnabledServices: iotServices,
},
rpmOstree: true,
pipelines: iotCommitPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"ostree-tree", "ostree-commit", "commit-archive"},
exports: []string{"commit-archive"},
}
iotOCIImgType = imageType{
name: "fedora-iot-container",
nameAliases: []string{"iot-container"},
filename: "container.tar",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
buildPkgsKey: iotBuildPackageSet,
osPkgsKey: iotCommitPackageSet,
containerPkgsKey: func(t *imageType) rpmmd.PackageSet {
return rpmmd.PackageSet{
Include: []string{"nginx"},
}
},
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
EnabledServices: iotServices,
},
rpmOstree: true,
bootISO: false,
pipelines: iotContainerPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"ostree-tree", "ostree-commit", "container-tree", "container"},
exports: []string{"container"},
}
iotInstallerImgType = imageType{
name: "fedora-iot-installer",
nameAliases: []string{"iot-installer"},
filename: "installer.iso",
mimeType: "application/x-iso9660-image",
packageSets: map[string]packageSetFunc{
buildPkgsKey: iotInstallerBuildPackageSet,
installerPkgsKey: iotInstallerPackageSet,
},
defaultImageConfig: &distro.ImageConfig{
Locale: "en_US.UTF-8",
EnabledServices: iotServices,
},
rpmOstree: true,
bootISO: true,
pipelines: iotInstallerPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"anaconda-tree", "bootiso-tree", "bootiso"},
exports: []string{"bootiso"},
}
qcow2ImgType = imageType{
name: "qcow2",
filename: "disk.qcow2",
mimeType: "application/x-qemu-disk",
packageSets: map[string]packageSetFunc{
buildPkgsKey: distroBuildPackageSet,
osPkgsKey: qcow2CommonPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
DefaultTarget: "multi-user.target",
EnabledServices: []string{
"cloud-init.service",
"cloud-config.service",
"cloud-final.service",
"cloud-init-local.service",
},
},
kernelOptions: defaultKernelOptions,
bootable: true,
defaultSize: 2 * GigaByte,
pipelines: qcow2Pipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "qcow2"},
exports: []string{"qcow2"},
basePartitionTables: defaultBasePartitionTables,
}
vhdImgType = imageType{
name: "vhd",
filename: "disk.vhd",
mimeType: "application/x-vhd",
packageSets: map[string]packageSetFunc{
buildPkgsKey: distroBuildPackageSet,
osPkgsKey: vhdCommonPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
Locale: "en_US.UTF-8",
EnabledServices: []string{
"sshd",
"waagent",
},
DefaultTarget: "multi-user.target",
DisabledServices: []string{
"proc-sys-fs-binfmt_misc.mount",
"loadmodules.service",
},
},
kernelOptions: defaultKernelOptions,
bootable: true,
defaultSize: 2 * GigaByte,
pipelines: vhdPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "vpc"},
exports: []string{"vpc"},
basePartitionTables: defaultBasePartitionTables,
}
vmdkImgType = imageType{
name: "vmdk",
filename: "disk.vmdk",
mimeType: "application/x-vmdk",
packageSets: map[string]packageSetFunc{
buildPkgsKey: distroBuildPackageSet,
osPkgsKey: vmdkCommonPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
Locale: "en_US.UTF-8",
EnabledServices: []string{
"cloud-init.service",
"cloud-config.service",
"cloud-final.service",
"cloud-init-local.service",
},
},
kernelOptions: defaultKernelOptions,
bootable: true,
defaultSize: 2 * GigaByte,
pipelines: vmdkPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "vmdk"},
exports: []string{"vmdk"},
basePartitionTables: defaultBasePartitionTables,
}
openstackImgType = imageType{
name: "openstack",
filename: "disk.qcow2",
mimeType: "application/x-qemu-disk",
packageSets: map[string]packageSetFunc{
buildPkgsKey: distroBuildPackageSet,
osPkgsKey: openstackCommonPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
Locale: "en_US.UTF-8",
EnabledServices: []string{
"cloud-init.service",
"cloud-config.service",
"cloud-final.service",
"cloud-init-local.service",
},
},
kernelOptions: defaultKernelOptions,
bootable: true,
defaultSize: 2 * GigaByte,
pipelines: openstackPipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "qcow2"},
exports: []string{"qcow2"},
basePartitionTables: defaultBasePartitionTables,
}
// default EC2 images config (common for all architectures)
defaultEc2ImageConfig = &distro.ImageConfig{
EnabledServices: []string{
"cloud-init.service",
},
DefaultTarget: "multi-user.target",
}
amiImgType = imageType{
name: "ami",
filename: "image.raw",
mimeType: "application/octet-stream",
packageSets: map[string]packageSetFunc{
buildPkgsKey: ec2BuildPackageSet,
osPkgsKey: ec2CommonPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: defaultEc2ImageConfig,
kernelOptions: defaultKernelOptions,
bootable: true,
bootType: distro.LegacyBootType,
defaultSize: 6 * GigaByte,
pipelines: ec2Pipelines,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image"},
exports: []string{"image"},
basePartitionTables: defaultBasePartitionTables,
}
containerImgType = imageType{
name: "container",
filename: "container.tar",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
buildPkgsKey: distroBuildPackageSet,
osPkgsKey: containerPackageSet,
},
packageSetChains: map[string][]string{
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
},
defaultImageConfig: &distro.ImageConfig{
NoSElinux: true,
ExcludeDocs: true,
Locale: "C.UTF-8",
Timezone: "Etc/UTC",
},
pipelines: containerPipelines,
bootable: false,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "container"},
exports: []string{"container"},
}
)
type distribution struct {
name string
product string
osVersion string
releaseVersion string
modulePlatformID string
vendor string
ostreeRefTmpl string
isolabelTmpl string
runner string
arches map[string]distro.Arch
defaultImageConfig *distro.ImageConfig
}
// Fedora based OS image configuration defaults
var defaultDistroImageConfig = &distro.ImageConfig{
Timezone: "UTC",
Locale: "en_US",
}
// distribution objects without the arches > image types
var distroMap = map[string]distribution{
fedora34Distribution: {
name: fedora34Distribution,
product: "Fedora",
osVersion: "34",
releaseVersion: "34",
modulePlatformID: "platform:f34",
vendor: "fedora",
ostreeRefTmpl: "fedora/34/%s/iot",
isolabelTmpl: "Fedora-34-BaseOS-%s",
runner: "org.osbuild.fedora34",
defaultImageConfig: defaultDistroImageConfig,
},
fedora35Distribution: {
name: fedora35Distribution,
product: "Fedora",
osVersion: "35",
releaseVersion: "35",
modulePlatformID: "platform:f35",
vendor: "fedora",
ostreeRefTmpl: "fedora/35/%s/iot",
isolabelTmpl: "Fedora-35-BaseOS-%s",
runner: "org.osbuild.fedora35",
defaultImageConfig: defaultDistroImageConfig,
},
fedora36Distribution: {
name: fedora36Distribution,
product: "Fedora",
osVersion: "36",
releaseVersion: "36",
modulePlatformID: "platform:f36",
vendor: "fedora",
ostreeRefTmpl: "fedora/36/%s/iot",
isolabelTmpl: "Fedora-36-BaseOS-%s",
runner: "org.osbuild.fedora36",
defaultImageConfig: defaultDistroImageConfig,
},
}
func (d *distribution) Name() string {
return d.name
}
func (d *distribution) Releasever() string {
return d.releaseVersion
}
func (d *distribution) ModulePlatformID() string {
return d.modulePlatformID
}
func (d *distribution) OSTreeRef() string {
return d.ostreeRefTmpl
}
func (d *distribution) ListArches() []string {
archNames := make([]string, 0, len(d.arches))
for name := range d.arches {
archNames = append(archNames, name)
}
sort.Strings(archNames)
return archNames
}
func (d *distribution) GetArch(name string) (distro.Arch, error) {
arch, exists := d.arches[name]
if !exists {
return nil, errors.New("invalid architecture: " + name)
}
return arch, nil
}
func (d *distribution) addArches(arches ...architecture) {
if d.arches == nil {
d.arches = map[string]distro.Arch{}
}
// Do not make copies of architectures, as opposed to image types,
// because architecture definitions are not used by more than a single
// distro definition.
for idx := range arches {
d.arches[arches[idx].name] = &arches[idx]
}
}
func (d *distribution) getDefaultImageConfig() *distro.ImageConfig {
return d.defaultImageConfig
}
type architecture struct {
distro *distribution
name string
imageTypes map[string]distro.ImageType
imageTypeAliases map[string]string
legacy string
bootType distro.BootType
}
func (a *architecture) Name() string {
return a.name
}
func (a *architecture) ListImageTypes() []string {
itNames := make([]string, 0, len(a.imageTypes))
for name := range a.imageTypes {
itNames = append(itNames, name)
}
sort.Strings(itNames)
return itNames
}
func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
t, exists := a.imageTypes[name]
if !exists {
aliasForName, exists := a.imageTypeAliases[name]
if !exists {
return nil, errors.New("invalid image type: " + name)
}
t, exists = a.imageTypes[aliasForName]
if !exists {
panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName))
}
}
return t, nil
}
func (a *architecture) addImageTypes(imageTypes ...imageType) {
if a.imageTypes == nil {
a.imageTypes = map[string]distro.ImageType{}
}
for idx := range imageTypes {
it := imageTypes[idx]
it.arch = a
a.imageTypes[it.name] = &it
for _, alias := range it.nameAliases {
if a.imageTypeAliases == nil {
a.imageTypeAliases = map[string]string{}
}
if existingAliasFor, exists := a.imageTypeAliases[alias]; exists {
panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor))
}
a.imageTypeAliases[alias] = it.name
}
}
}
func (a *architecture) Distro() distro.Distro {
return a.distro
}
type pipelinesFunc func(m *manifest.Manifest, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetChains map[string][]rpmmd.PackageSet, rng *rand.Rand) ([]manifest.Pipeline, error)
type packageSetFunc func(t *imageType) rpmmd.PackageSet
type imageType struct {
arch *architecture
name string
nameAliases []string
filename string
mimeType string
packageSets map[string]packageSetFunc
packageSetChains map[string][]string
defaultImageConfig *distro.ImageConfig
kernelOptions string
defaultSize uint64
buildPipelines []string
payloadPipelines []string
exports []string
pipelines pipelinesFunc
// bootISO: installable ISO
bootISO bool
// rpmOstree: iot/ostree
rpmOstree bool
// bootable image
bootable bool
// If set to a value, it is preferred over the architecture value
bootType distro.BootType
// List of valid arches for the image type
basePartitionTables distro.BasePartitionTableMap
}
func (t *imageType) Name() string {
return t.name
}
func (t *imageType) Arch() distro.Arch {
return t.arch
}
func (t *imageType) Filename() string {
return t.filename
}
func (t *imageType) MIMEType() string {
return t.mimeType
}
func (t *imageType) OSTreeRef() string {
d := t.arch.distro
if t.rpmOstree {
return fmt.Sprintf(d.ostreeRefTmpl, t.arch.Name())
}
return ""
}
func (t *imageType) 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 *imageType) getPackages(name string) rpmmd.PackageSet {
getter := t.packageSets[name]
if getter == nil {
return rpmmd.PackageSet{}
}
return getter(t)
}
func (t *imageType) PackageSets(bp blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig) map[string][]rpmmd.PackageSet {
// merge package sets that appear in the image type with the package sets
// of the same name from the distro and arch
mergedSets := make(map[string]rpmmd.PackageSet)
imageSets := t.packageSets
for name := range imageSets {
mergedSets[name] = t.getPackages(name)
}
if _, hasPackages := imageSets[osPkgsKey]; !hasPackages {
// should this be possible??
mergedSets[osPkgsKey] = rpmmd.PackageSet{}
}
// every image type must define a 'build' package set
if _, hasBuild := imageSets[buildPkgsKey]; !hasBuild {
panic(fmt.Sprintf("'%s' image type has no '%s' package set defined", t.name, buildPkgsKey))
}
// blueprint packages
bpPackages := bp.GetPackagesEx(t.rpmOstree || t.bootable)
timezone, _ := bp.Customizations.GetTimezoneSettings()
if timezone != nil {
bpPackages = append(bpPackages, "chrony")
}
// if we have file system customization that will need to a new mount point
// the layout is converted to LVM so we need to corresponding packages
if t.bootable && !t.rpmOstree {
pt, exists := t.basePartitionTables[t.arch.Name()]
if !exists {
panic(fmt.Sprintf("unknown architecture with boot type: %s %s", t.arch.Name(), t.bootType))
}
haveNewMountpoint := false
if fs := bp.Customizations.GetFilesystems(); fs != nil {
for i := 0; !haveNewMountpoint && i < len(fs); i++ {
haveNewMountpoint = !pt.ContainsMountpoint(fs[i].Mountpoint)
}
}
if haveNewMountpoint {
bpPackages = append(bpPackages, "lvm2")
}
}
// depsolve bp packages separately
// bp packages aren't restricted by exclude lists
mergedSets[blueprintPkgsKey] = rpmmd.PackageSet{Include: bpPackages}
kernel := bp.Customizations.GetKernel().Name
// add bp kernel to main OS package set to avoid duplicate kernels,
// but we don't want to add the kernel for the container artefact
if t.rpmOstree || t.bootable {
mergedSets[osPkgsKey] = mergedSets[osPkgsKey].Append(rpmmd.PackageSet{Include: []string{kernel}})
}
// create a manifest object and instantiate it with the computed packageSetChains
manifest, err := t.initializeManifest(bp.Customizations, options, repos, distro.MakePackageSetChains(t, mergedSets, repos), 0)
if err != nil {
// TODO: handle manifest initialization errors more gracefully, we
// refuse to initialize manifests with invalid config.
return nil
}
manifestChains := manifest.GetPackageSetChains()
// the returned package set chains are indexed by pipeline
// name, we need to reindex by package set name
// TODO: drop translation, see Manifest()
distroChains := make(map[string][]rpmmd.PackageSet)
for name, chain := range manifestChains {
switch name {
case "os":
name = osPkgsKey
case "ostree-tree":
name = osPkgsKey
case "container-tree":
name = containerPkgsKey
case "anaconda-tree":
name = installerPkgsKey
case "build":
name = buildPkgsKey
default:
panic(fmt.Sprintf("unknown pacakge set name: %s", name))
}
distroChains[name] = chain
}
return distroChains
}
func (t *imageType) BuildPipelines() []string {
return t.buildPipelines
}
func (t *imageType) PayloadPipelines() []string {
return t.payloadPipelines
}
func (t *imageType) PayloadPackageSets() []string {
return []string{blueprintPkgsKey}
}
func (t *imageType) PackageSetsChains() map[string][]string {
return t.packageSetChains
}
func (t *imageType) Exports() []string {
if len(t.exports) > 0 {
return t.exports
}
return []string{"assembler"}
}
// getBootType returns the BootType which should be used for this particular
// combination of architecture and image type.
func (t *imageType) getBootType() distro.BootType {
bootType := t.arch.bootType
if t.bootType != distro.UnsetBootType {
bootType = t.bootType
}
return bootType
}
func (t *imageType) supportsUEFI() bool {
bootType := t.getBootType()
return bootType == distro.HybridBootType || bootType == distro.UEFIBootType
}
func (t *imageType) getPartitionTable(
mountpoints []blueprint.FilesystemCustomization,
options distro.ImageOptions,
rng *rand.Rand,
) (*disk.PartitionTable, error) {
basePartitionTable, exists := t.basePartitionTables[t.arch.Name()]
if !exists {
return nil, fmt.Errorf("unknown arch: " + t.arch.Name())
}
imageSize := t.Size(options.Size)
lvmify := !t.rpmOstree
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, rng)
}
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {
// ensure that image always returns non-nil default config
imageConfig := t.defaultImageConfig
if imageConfig == nil {
imageConfig = &distro.ImageConfig{}
}
return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig())
}
func (t *imageType) PartitionType() string {
basePartitionTable, exists := t.basePartitionTables[t.arch.Name()]
if !exists {
return ""
}
return basePartitionTable.Type
}
func (t *imageType) initializeManifest(customizations *blueprint.Customizations,
options distro.ImageOptions,
repos []rpmmd.RepoConfig,
packageSetChains map[string][]rpmmd.PackageSet,
seed int64) (*manifest.Manifest, error) {
if err := t.checkOptions(customizations, options); err != nil {
return nil, err
}
source := rand.NewSource(seed)
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(source)
manifest := manifest.New()
_, err := t.pipelines(&manifest, t, customizations, options, repos, packageSetChains, rng)
if err != nil {
return nil, err
}
return &manifest, nil
}
func (t *imageType) Manifest(customizations *blueprint.Customizations,
options distro.ImageOptions,
repos []rpmmd.RepoConfig,
distroPackageSets map[string][]rpmmd.PackageSpec,
seed int64) (distro.Manifest, error) {
manifest, err := t.initializeManifest(customizations, options, repos, nil, seed)
if err != nil {
return distro.Manifest{}, err
}
// TODO: drop transaltion, see GetPackageSets()
manifestPackageSets := make(map[string][]rpmmd.PackageSpec)
for name, set := range distroPackageSets {
switch name {
case osPkgsKey:
manifestPackageSets["os"] = set
manifestPackageSets["ostree-tree"] = set
case containerPkgsKey:
manifestPackageSets["container-tree"] = set
case installerPkgsKey:
manifestPackageSets["anaconda-tree"] = set
case buildPkgsKey:
manifestPackageSets["build"] = set
default:
panic(fmt.Sprintf("unknown pacakge set name: %s", name))
}
}
return manifest.Serialize(manifestPackageSets)
}
func isMountpointAllowed(mountpoint string) bool {
for _, allowed := range mountpointAllowList {
match, _ := path.Match(allowed, mountpoint)
if match {
return true
}
// ensure that only clean mountpoints
// are valid
if strings.Contains(mountpoint, "//") {
return false
}
match = strings.HasPrefix(mountpoint, allowed+"/")
if allowed != "/" && match {
return true
}
}
return false
}
// 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 && t.rpmOstree {
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 == "iot-installer" || t.name == "fedora-iot-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 !isMountpointAllowed(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 NewHostDistro(name, modulePlatformID, ostreeRef string) distro.Distro {
return newDistro(name)
}
// New creates a new distro object, defining the supported architectures and image types
func NewF34() distro.Distro {
return newDistro(fedora34Distribution)
}
func NewF35() distro.Distro {
return newDistro(fedora35Distribution)
}
func NewF36() distro.Distro {
return newDistro(fedora36Distribution)
}
func newDistro(distroName string) distro.Distro {
rd := distroMap[distroName]
// Architecture definitions
x86_64 := architecture{
name: distro.X86_64ArchName,
distro: &rd,
legacy: "i386-pc",
bootType: distro.HybridBootType,
}
aarch64 := architecture{
name: distro.Aarch64ArchName,
distro: &rd,
bootType: distro.UEFIBootType,
}
s390x := architecture{
distro: &rd,
name: distro.S390xArchName,
bootType: distro.LegacyBootType,
}
ociImgType := qcow2ImgType
ociImgType.name = "oci"
x86_64.addImageTypes(
amiImgType,
containerImgType,
qcow2ImgType,
openstackImgType,
vhdImgType,
vmdkImgType,
ociImgType,
iotOCIImgType,
iotCommitImgType,
iotInstallerImgType,
)
aarch64.addImageTypes(
amiImgType,
containerImgType,
qcow2ImgType,
openstackImgType,
ociImgType,
iotCommitImgType,
iotOCIImgType,
iotInstallerImgType,
)
s390x.addImageTypes()
rd.addArches(x86_64, aarch64, s390x)
return &rd
}