Some pipeline lists weren't updated when the image types were rewritten. Discovered now from the new test and fixed.
974 lines
27 KiB
Go
974 lines
27 KiB
Go
package fedora
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
|
"github.com/osbuild/osbuild-composer/internal/common"
|
|
"github.com/osbuild/osbuild-composer/internal/container"
|
|
"github.com/osbuild/osbuild-composer/internal/disk"
|
|
"github.com/osbuild/osbuild-composer/internal/distro"
|
|
"github.com/osbuild/osbuild-composer/internal/environment"
|
|
"github.com/osbuild/osbuild-composer/internal/image"
|
|
"github.com/osbuild/osbuild-composer/internal/manifest"
|
|
"github.com/osbuild/osbuild-composer/internal/oscap"
|
|
"github.com/osbuild/osbuild-composer/internal/platform"
|
|
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
|
"github.com/osbuild/osbuild-composer/internal/runner"
|
|
"github.com/osbuild/osbuild-composer/internal/workload"
|
|
)
|
|
|
|
const (
|
|
// package set names
|
|
|
|
// 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"
|
|
|
|
//Kernel options for ami, qcow2, openstack, vhd and vmdk types
|
|
defaultKernelOptions = "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0"
|
|
)
|
|
|
|
var (
|
|
oscapProfileAllowList = []oscap.Profile{
|
|
oscap.Ospp,
|
|
oscap.PciDss,
|
|
oscap.Standard,
|
|
}
|
|
|
|
// 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
|
|
imageInstallerImgType = imageType{
|
|
name: "image-installer",
|
|
nameAliases: []string{"fedora-image-installer"},
|
|
filename: "installer.iso",
|
|
mimeType: "application/x-iso9660-image",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: bareMetalPackageSet,
|
|
installerPkgsKey: imageInstallerPackageSet,
|
|
},
|
|
bootable: true,
|
|
bootISO: true,
|
|
rpmOstree: false,
|
|
image: imageInstallerImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "os", "bootiso-tree", "bootiso"},
|
|
exports: []string{"bootiso"},
|
|
}
|
|
|
|
iotCommitImgType = imageType{
|
|
name: "iot-commit",
|
|
nameAliases: []string{"fedora-iot-commit"},
|
|
filename: "commit.tar",
|
|
mimeType: "application/x-tar",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: iotCommitPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
EnabledServices: iotServices,
|
|
},
|
|
rpmOstree: true,
|
|
image: iotCommitImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"os", "ostree-commit", "commit-archive"},
|
|
exports: []string{"commit-archive"},
|
|
}
|
|
|
|
iotOCIImgType = imageType{
|
|
name: "iot-container",
|
|
nameAliases: []string{"fedora-iot-container"},
|
|
filename: "container.tar",
|
|
mimeType: "application/x-tar",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: iotCommitPackageSet,
|
|
containerPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
|
return rpmmd.PackageSet{}
|
|
},
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
EnabledServices: iotServices,
|
|
},
|
|
rpmOstree: true,
|
|
bootISO: false,
|
|
image: iotContainerImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"os", "ostree-commit", "container-tree", "container"},
|
|
exports: []string{"container"},
|
|
}
|
|
|
|
iotInstallerImgType = imageType{
|
|
name: "iot-installer",
|
|
nameAliases: []string{"fedora-iot-installer"},
|
|
filename: "installer.iso",
|
|
mimeType: "application/x-iso9660-image",
|
|
packageSets: map[string]packageSetFunc{
|
|
installerPkgsKey: iotInstallerPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
Locale: common.ToPtr("en_US.UTF-8"),
|
|
EnabledServices: iotServices,
|
|
},
|
|
rpmOstree: true,
|
|
bootISO: true,
|
|
image: iotInstallerImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"},
|
|
exports: []string{"bootiso"},
|
|
}
|
|
|
|
iotRawImgType = imageType{
|
|
name: "iot-raw-image",
|
|
nameAliases: []string{"fedora-iot-raw-image"},
|
|
filename: "image.raw.xz",
|
|
mimeType: "application/xz",
|
|
packageSets: map[string]packageSetFunc{},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
Locale: common.ToPtr("en_US.UTF-8"),
|
|
},
|
|
defaultSize: 10 * common.GibiByte,
|
|
rpmOstree: true,
|
|
bootable: true,
|
|
image: iotRawImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"image-tree", "image", "xz"},
|
|
exports: []string{"xz"},
|
|
basePartitionTables: iotBasePartitionTables,
|
|
}
|
|
|
|
qcow2ImgType = imageType{
|
|
name: "qcow2",
|
|
filename: "disk.qcow2",
|
|
mimeType: "application/x-qemu-disk",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: qcow2CommonPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
DefaultTarget: common.ToPtr("multi-user.target"),
|
|
EnabledServices: []string{
|
|
"cloud-init.service",
|
|
"cloud-config.service",
|
|
"cloud-final.service",
|
|
"cloud-init-local.service",
|
|
},
|
|
},
|
|
kernelOptions: defaultKernelOptions,
|
|
bootable: true,
|
|
defaultSize: 2 * common.GibiByte,
|
|
image: liveImage,
|
|
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{
|
|
osPkgsKey: vhdCommonPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
Locale: common.ToPtr("en_US.UTF-8"),
|
|
EnabledServices: []string{
|
|
"sshd",
|
|
},
|
|
DefaultTarget: common.ToPtr("multi-user.target"),
|
|
DisabledServices: []string{
|
|
"proc-sys-fs-binfmt_misc.mount",
|
|
"loadmodules.service",
|
|
},
|
|
},
|
|
kernelOptions: defaultKernelOptions,
|
|
bootable: true,
|
|
defaultSize: 2 * common.GibiByte,
|
|
image: liveImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"os", "image", "vpc"},
|
|
exports: []string{"vpc"},
|
|
basePartitionTables: defaultBasePartitionTables,
|
|
environment: &environment.Azure{},
|
|
}
|
|
|
|
vmdkImgType = imageType{
|
|
name: "vmdk",
|
|
filename: "disk.vmdk",
|
|
mimeType: "application/x-vmdk",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: vmdkCommonPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
Locale: common.ToPtr("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 * common.GibiByte,
|
|
image: liveImage,
|
|
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{
|
|
osPkgsKey: openstackCommonPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
Locale: common.ToPtr("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 * common.GibiByte,
|
|
image: liveImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"os", "image", "qcow2"},
|
|
exports: []string{"qcow2"},
|
|
basePartitionTables: defaultBasePartitionTables,
|
|
}
|
|
|
|
// default EC2 images config (common for all architectures)
|
|
defaultEc2ImageConfig = &distro.ImageConfig{
|
|
DefaultTarget: common.ToPtr("multi-user.target"),
|
|
}
|
|
|
|
amiImgType = imageType{
|
|
name: "ami",
|
|
filename: "image.raw",
|
|
mimeType: "application/octet-stream",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: ec2CommonPackageSet,
|
|
},
|
|
defaultImageConfig: defaultEc2ImageConfig,
|
|
kernelOptions: defaultKernelOptions,
|
|
bootable: true,
|
|
defaultSize: 6 * common.GibiByte,
|
|
image: liveImage,
|
|
buildPipelines: []string{"build"},
|
|
payloadPipelines: []string{"os", "image"},
|
|
exports: []string{"image"},
|
|
basePartitionTables: defaultBasePartitionTables,
|
|
environment: &environment.EC2{},
|
|
}
|
|
|
|
containerImgType = imageType{
|
|
name: "container",
|
|
filename: "container.tar",
|
|
mimeType: "application/x-tar",
|
|
packageSets: map[string]packageSetFunc{
|
|
osPkgsKey: containerPackageSet,
|
|
},
|
|
defaultImageConfig: &distro.ImageConfig{
|
|
NoSElinux: common.ToPtr(true),
|
|
ExcludeDocs: common.ToPtr(true),
|
|
Locale: common.ToPtr("C.UTF-8"),
|
|
Timezone: common.ToPtr("Etc/UTC"),
|
|
},
|
|
image: containerImage,
|
|
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
|
|
ostreeRefTmpl string
|
|
isolabelTmpl string
|
|
runner runner.Runner
|
|
arches map[string]distro.Arch
|
|
defaultImageConfig *distro.ImageConfig
|
|
}
|
|
|
|
// Fedora based OS image configuration defaults
|
|
var defaultDistroImageConfig = &distro.ImageConfig{
|
|
Timezone: common.ToPtr("UTC"),
|
|
Locale: common.ToPtr("en_US"),
|
|
}
|
|
|
|
func getDistro(version int) distribution {
|
|
return distribution{
|
|
name: fmt.Sprintf("fedora-%d", version),
|
|
product: "Fedora",
|
|
osVersion: strconv.Itoa(version),
|
|
releaseVersion: strconv.Itoa(version),
|
|
modulePlatformID: fmt.Sprintf("platform:f%d", version),
|
|
ostreeRefTmpl: fmt.Sprintf("fedora/%d/%%s/iot", version),
|
|
isolabelTmpl: fmt.Sprintf("Fedora-%d-BaseOS-%%s", version),
|
|
runner: &runner.Fedora{Version: uint64(version)},
|
|
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
|
|
}
|
|
|
|
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(platform platform.Platform, imageTypes ...imageType) {
|
|
if a.imageTypes == nil {
|
|
a.imageTypes = map[string]distro.ImageType{}
|
|
}
|
|
for idx := range imageTypes {
|
|
it := imageTypes[idx]
|
|
it.arch = a
|
|
it.platform = platform
|
|
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 imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.Spec, rng *rand.Rand) (image.ImageKind, error)
|
|
|
|
type packageSetFunc func(t *imageType) rpmmd.PackageSet
|
|
|
|
type imageType struct {
|
|
arch *architecture
|
|
platform platform.Platform
|
|
environment environment.Environment
|
|
name string
|
|
nameAliases []string
|
|
filename string
|
|
mimeType string
|
|
packageSets map[string]packageSetFunc
|
|
defaultImageConfig *distro.ImageConfig
|
|
kernelOptions string
|
|
defaultSize uint64
|
|
buildPipelines []string
|
|
payloadPipelines []string
|
|
exports []string
|
|
image imageFunc
|
|
|
|
// bootISO: installable ISO
|
|
bootISO bool
|
|
// rpmOstree: iot/ostree
|
|
rpmOstree bool
|
|
// bootable image
|
|
bootable bool
|
|
// 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 {
|
|
// Microsoft Azure requires vhd images to be rounded up to the nearest MB
|
|
if t.name == "vhd" && size%common.MebiByte != 0 {
|
|
size = (size/common.MebiByte + 1) * common.MebiByte
|
|
}
|
|
if size == 0 {
|
|
size = t.defaultSize
|
|
}
|
|
return size
|
|
}
|
|
|
|
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
|
|
packageSets := make(map[string]rpmmd.PackageSet)
|
|
|
|
for name, getter := range t.packageSets {
|
|
packageSets[name] = getter(t)
|
|
}
|
|
|
|
// amend with repository information
|
|
globalRepos := make([]rpmmd.RepoConfig, 0)
|
|
for _, repo := range repos {
|
|
if len(repo.PackageSets) > 0 {
|
|
// only apply the repo to the listed package sets
|
|
for _, psName := range repo.PackageSets {
|
|
ps := packageSets[psName]
|
|
ps.Repositories = append(ps.Repositories, repo)
|
|
packageSets[psName] = ps
|
|
}
|
|
} else {
|
|
// no package sets were listed, so apply the repo
|
|
// to all package sets
|
|
globalRepos = append(globalRepos, repo)
|
|
}
|
|
}
|
|
|
|
// In case of Cloud API, this method is called before the ostree commit
|
|
// is resolved. Unfortunately, initializeManifest when called for
|
|
// an ostree installer returns an error.
|
|
//
|
|
// Work around this by providing a dummy FetchChecksum to convince the
|
|
// method that it's fine to initialize the manifest. Note that the ostree
|
|
// content has no effect on the package sets, so this is fine.
|
|
//
|
|
// See: https://github.com/osbuild/osbuild-composer/issues/3125
|
|
//
|
|
// TODO: Remove me when it's possible the get the package set chain without
|
|
// resolving the ostree reference before. Also remove the test for
|
|
// this workaround
|
|
if t.rpmOstree && t.bootISO && options.OSTree.FetchChecksum == "" {
|
|
options.OSTree.FetchChecksum = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
logrus.Warn("FIXME: Requesting package sets for iot-installer without a resolved ostree ref. Faking one.")
|
|
}
|
|
|
|
// Similar to above, for edge-commit and edge-container, we need to set an
|
|
// ImageRef in order to properly initialize the manifest and package
|
|
// selection.
|
|
options.OSTree.ImageRef = t.OSTreeRef()
|
|
|
|
// create a temporary container spec array with the info from the blueprint
|
|
// to initialize the manifest
|
|
containers := make([]container.Spec, len(bp.Containers))
|
|
for idx := range bp.Containers {
|
|
containers[idx] = container.Spec{
|
|
Source: bp.Containers[idx].Source,
|
|
TLSVerify: bp.Containers[idx].TLSVerify,
|
|
LocalName: bp.Containers[idx].Name,
|
|
}
|
|
}
|
|
|
|
// create a manifest object and instantiate it with the computed packageSetChains
|
|
manifest, err := t.initializeManifest(&bp, options, globalRepos, packageSets, containers, 0)
|
|
if err != nil {
|
|
// TODO: handle manifest initialization errors more gracefully, we
|
|
// refuse to initialize manifests with invalid config.
|
|
logrus.Errorf("Initializing the manifest failed for %s (%s/%s): %v", t.Name(), t.arch.distro.Name(), t.arch.Name(), err)
|
|
return nil
|
|
}
|
|
|
|
return manifest.GetPackageSetChains()
|
|
}
|
|
|
|
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 make(map[string][]string)
|
|
}
|
|
|
|
func (t *imageType) Exports() []string {
|
|
if len(t.exports) > 0 {
|
|
return t.exports
|
|
}
|
|
return []string{"assembler"}
|
|
}
|
|
|
|
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(bp *blueprint.Blueprint,
|
|
options distro.ImageOptions,
|
|
repos []rpmmd.RepoConfig,
|
|
packageSets map[string]rpmmd.PackageSet,
|
|
containers []container.Spec,
|
|
seed int64) (*manifest.Manifest, error) {
|
|
|
|
if err := t.checkOptions(bp.Customizations, options, containers); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: let image types specify valid workloads, rather than
|
|
// always assume Custom.
|
|
w := &workload.Custom{
|
|
BaseWorkload: workload.BaseWorkload{
|
|
Repos: packageSets[blueprintPkgsKey].Repositories,
|
|
},
|
|
Packages: bp.GetPackagesEx(false),
|
|
}
|
|
if services := bp.Customizations.GetServices(); services != nil {
|
|
w.Services = services.Enabled
|
|
w.DisabledServices = services.Disabled
|
|
}
|
|
|
|
source := rand.NewSource(seed)
|
|
// math/rand is good enough in this case
|
|
/* #nosec G404 */
|
|
rng := rand.New(source)
|
|
|
|
img, err := t.image(w, t, bp.Customizations, options, packageSets, containers, rng)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
manifest := manifest.New()
|
|
_, err = img.InstantiateManifest(&manifest, repos, t.arch.distro.runner, rng)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &manifest, err
|
|
}
|
|
|
|
func (t *imageType) Manifest(customizations *blueprint.Customizations,
|
|
options distro.ImageOptions,
|
|
repos []rpmmd.RepoConfig,
|
|
packageSets map[string][]rpmmd.PackageSpec,
|
|
containers []container.Spec,
|
|
seed int64) (distro.Manifest, error) {
|
|
|
|
bp := &blueprint.Blueprint{Name: "empty blueprint"}
|
|
err := bp.Initialize()
|
|
if err != nil {
|
|
panic("could not initialize empty blueprint: " + err.Error())
|
|
}
|
|
bp.Customizations = customizations
|
|
|
|
manifest, err := t.initializeManifest(bp, options, repos, nil, containers, seed)
|
|
if err != nil {
|
|
return distro.Manifest{}, err
|
|
}
|
|
|
|
return manifest.Serialize(packageSets)
|
|
}
|
|
|
|
// checkOptions checks the validity and compatibility of options and customizations for the image type.
|
|
func (t *imageType) checkOptions(customizations *blueprint.Customizations, options distro.ImageOptions, containers []container.Spec) error {
|
|
|
|
// we do not support embedding containers on ostree-derived images, only on commits themselves
|
|
if len(containers) > 0 && t.rpmOstree && (t.name != "iot-commit" && t.name != "iot-container") {
|
|
return fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
|
|
}
|
|
|
|
if t.bootISO && t.rpmOstree {
|
|
// check the checksum instead of the URL, because the URL should have been used to resolve the checksum and we need both
|
|
if options.OSTree.FetchChecksum == "" {
|
|
return fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name)
|
|
}
|
|
}
|
|
|
|
// BootISO's have limited support for customizations.
|
|
// TODO: Support kernel name selection for image-installer
|
|
if t.bootISO {
|
|
if t.name == "iot-installer" || t.name == "image-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")
|
|
}
|
|
|
|
err := disk.CheckMountpoints(mountpoints, disk.MountpointPolicies)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if osc := customizations.GetOpenSCAP(); osc != nil {
|
|
supported := oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList)
|
|
if !supported {
|
|
return fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID))
|
|
}
|
|
if t.rpmOstree {
|
|
return fmt.Errorf("OpenSCAP customizations are not supported for ostree types")
|
|
}
|
|
if osc.DataStream == "" {
|
|
return fmt.Errorf("OpenSCAP datastream cannot be empty")
|
|
}
|
|
if osc.ProfileID == "" {
|
|
return fmt.Errorf("OpenSCAP profile cannot be empty")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// New creates a new distro object, defining the supported architectures and image types
|
|
func NewF36() distro.Distro {
|
|
return newDistro(36)
|
|
}
|
|
func NewF37() distro.Distro {
|
|
return newDistro(37)
|
|
}
|
|
func NewF38() distro.Distro {
|
|
return newDistro(38)
|
|
}
|
|
|
|
func newDistro(version int) distro.Distro {
|
|
rd := getDistro(version)
|
|
|
|
// Architecture definitions
|
|
x86_64 := architecture{
|
|
name: distro.X86_64ArchName,
|
|
distro: &rd,
|
|
}
|
|
|
|
aarch64 := architecture{
|
|
name: distro.Aarch64ArchName,
|
|
distro: &rd,
|
|
}
|
|
|
|
s390x := architecture{
|
|
distro: &rd,
|
|
name: distro.S390xArchName,
|
|
}
|
|
|
|
ociImgType := qcow2ImgType
|
|
ociImgType.name = "oci"
|
|
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BIOS: true,
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_QCOW2,
|
|
QCOW2Compat: "1.1",
|
|
},
|
|
},
|
|
qcow2ImgType,
|
|
ociImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BIOS: true,
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_QCOW2,
|
|
},
|
|
},
|
|
openstackImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BIOS: true,
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_VHD,
|
|
},
|
|
},
|
|
vhdImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BIOS: true,
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_VMDK,
|
|
},
|
|
},
|
|
vmdkImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BIOS: true,
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_RAW,
|
|
},
|
|
},
|
|
amiImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{},
|
|
containerImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BasePlatform: platform.BasePlatform{
|
|
FirmwarePackages: []string{
|
|
"microcode_ctl", // ??
|
|
"iwl1000-firmware",
|
|
"iwl100-firmware",
|
|
"iwl105-firmware",
|
|
"iwl135-firmware",
|
|
"iwl2000-firmware",
|
|
"iwl2030-firmware",
|
|
"iwl3160-firmware",
|
|
"iwl5000-firmware",
|
|
"iwl5150-firmware",
|
|
"iwl6000-firmware",
|
|
"iwl6050-firmware",
|
|
},
|
|
},
|
|
BIOS: true,
|
|
UEFIVendor: "fedora",
|
|
},
|
|
iotOCIImgType,
|
|
iotCommitImgType,
|
|
iotInstallerImgType,
|
|
imageInstallerImgType,
|
|
)
|
|
x86_64.addImageTypes(
|
|
&platform.X86{
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_RAW,
|
|
},
|
|
BIOS: false,
|
|
UEFIVendor: "fedora",
|
|
},
|
|
iotRawImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_RAW,
|
|
},
|
|
},
|
|
amiImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_QCOW2,
|
|
QCOW2Compat: "1.1",
|
|
},
|
|
},
|
|
qcow2ImgType,
|
|
ociImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{
|
|
UEFIVendor: "fedora",
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_QCOW2,
|
|
},
|
|
},
|
|
openstackImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{},
|
|
containerImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{
|
|
BasePlatform: platform.BasePlatform{
|
|
FirmwarePackages: []string{
|
|
"uboot-images-armv8", // ??
|
|
"bcm283x-firmware",
|
|
"arm-image-installer", // ??
|
|
},
|
|
},
|
|
UEFIVendor: "fedora",
|
|
},
|
|
iotCommitImgType,
|
|
iotOCIImgType,
|
|
iotInstallerImgType,
|
|
imageInstallerImgType,
|
|
)
|
|
aarch64.addImageTypes(
|
|
&platform.Aarch64{
|
|
BasePlatform: platform.BasePlatform{
|
|
ImageFormat: platform.FORMAT_RAW,
|
|
},
|
|
UEFIVendor: "fedora",
|
|
},
|
|
iotRawImgType,
|
|
)
|
|
|
|
s390x.addImageTypes(nil)
|
|
|
|
rd.addArches(x86_64, aarch64, s390x)
|
|
return &rd
|
|
}
|