platform: introduce hardware platform abstraction

These objects describes the hardware an image runs on. Including
 - architecture
 - bootloader
 - required firmware

Use the platform abstraction to move firmware packages out of the package set
definitions.
This commit is contained in:
Tom Gundersen 2022-07-05 17:43:03 +01:00
parent 682481d4d7
commit ec8cc01f95
9 changed files with 227 additions and 141 deletions

View file

@ -2,6 +2,7 @@ package main
import (
"github.com/osbuild/osbuild-composer/internal/manifest"
"github.com/osbuild/osbuild-composer/internal/platform"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
@ -31,7 +32,7 @@ func MyManifest(m *manifest.Manifest, options *MyOptions, repos []rpmmd.RepoConf
build := manifest.NewBuildPipeline(m, runner, repos)
// create a non-bootable OS tree containing the `core` comps group
os := manifest.NewOSPipeline(m, build, manifest.ARCH_X86_64, repos)
os := manifest.NewOSPipeline(m, build, &platform.X86{}, repos)
os.ExtraBasePackages = []string{
"@core",
}

View file

@ -11,6 +11,7 @@ import (
"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/platform"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
@ -273,7 +274,6 @@ var (
defaultImageConfig: defaultEc2ImageConfig,
kernelOptions: defaultKernelOptions,
bootable: true,
bootType: distro.LegacyBootType,
defaultSize: 6 * GigaByte,
manifest: ec2Manifest,
buildPipelines: []string{"build"},
@ -421,8 +421,6 @@ type architecture struct {
name string
imageTypes map[string]distro.ImageType
imageTypeAliases map[string]string
legacy string
bootType distro.BootType
}
func (a *architecture) Name() string {
@ -453,13 +451,14 @@ func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
return t, nil
}
func (a *architecture) addImageTypes(imageTypes ...imageType) {
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 {
@ -483,6 +482,7 @@ type packageSetFunc func(t *imageType) rpmmd.PackageSet
type imageType struct {
arch *architecture
platform platform.Platform
name string
nameAliases []string
filename string
@ -503,8 +503,6 @@ type imageType struct {
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
}
@ -581,9 +579,9 @@ func (t *imageType) PackageSets(bp blueprint.Blueprint, options distro.ImageOpti
// the layout is converted to LVM so we need to corresponding packages
if t.bootable && !t.rpmOstree {
pt, exists := t.basePartitionTables[t.arch.Name()]
pt, exists := t.basePartitionTables[t.platform.GetArch().String()]
if !exists {
panic(fmt.Sprintf("unknown architecture with boot type: %s %s", t.arch.Name(), t.bootType))
panic(fmt.Sprintf("unknown no partition table for architecture %s", t.platform.GetArch().String()))
}
haveNewMountpoint := false
@ -643,23 +641,6 @@ func (t *imageType) Exports() []string {
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 {
if bootType == distro.HybridBootType {
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,
@ -794,51 +775,99 @@ func newDistro(distroName string) distro.Distro {
// Architecture definitions
x86_64 := architecture{
name: distro.X86_64ArchName,
distro: &rd,
legacy: "i386-pc",
bootType: distro.HybridBootType,
name: distro.X86_64ArchName,
distro: &rd,
}
aarch64 := architecture{
name: distro.Aarch64ArchName,
distro: &rd,
bootType: distro.UEFIBootType,
name: distro.Aarch64ArchName,
distro: &rd,
}
s390x := architecture{
distro: &rd,
name: distro.S390xArchName,
bootType: distro.LegacyBootType,
distro: &rd,
name: distro.S390xArchName,
}
ociImgType := qcow2ImgType
ociImgType.name = "oci"
x86_64.addImageTypes(
amiImgType,
containerImgType,
&platform.X86{
BIOS: true,
UEFIVendor: "fedora",
},
qcow2ImgType,
openstackImgType,
vhdImgType,
vmdkImgType,
ociImgType,
)
x86_64.addImageTypes(
&platform.X86{
BIOS: true,
},
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,
)
aarch64.addImageTypes(
&platform.Aarch64{
UEFIVendor: "fedora",
},
amiImgType,
containerImgType,
qcow2ImgType,
openstackImgType,
ociImgType,
)
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,
)
s390x.addImageTypes()
s390x.addImageTypes(nil)
rd.addArches(x86_64, aarch64, s390x)
return &rd

View file

@ -208,19 +208,7 @@ func osPipeline(m *manifest.Manifest,
imageConfig := t.getDefaultImageConfig()
var arch manifest.Arch
switch t.Arch().Name() {
case distro.X86_64ArchName:
arch = manifest.ARCH_X86_64
case distro.Aarch64ArchName:
arch = manifest.ARCH_AARCH64
case distro.Ppc64leArchName:
arch = manifest.ARCH_PPC64LE
case distro.S390xArchName:
arch = manifest.ARCH_S390X
}
pl := manifest.NewOSPipeline(m, buildPipeline, arch, repos)
pl := manifest.NewOSPipeline(m, buildPipeline, t.platform, repos)
if t.bootable {
var err error
@ -232,12 +220,6 @@ func osPipeline(m *manifest.Manifest,
}
if t.bootable || t.rpmOstree {
if t.supportsUEFI() {
pl.UEFIVendor = t.arch.distro.vendor
}
pl.BIOSPlatform = t.arch.legacy
pl.KernelName = c.GetKernel().Name
var kernelOptions []string

View file

@ -235,46 +235,11 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"iwlax2xx-firmware",
},
}
switch t.Arch().Name() {
case distro.X86_64ArchName:
ps = ps.Append(x8664IOTCommitPackageSet())
case distro.Aarch64ArchName:
ps = ps.Append(aarch64IOTCommitPackageSet())
}
return ps
}
func x8664IOTCommitPackageSet() rpmmd.PackageSet {
return rpmmd.PackageSet{
Include: []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",
},
}
}
func aarch64IOTCommitPackageSet() rpmmd.PackageSet {
return rpmmd.PackageSet{
Include: []string{
"uboot-images-armv8",
"bcm283x-firmware",
"arm-image-installer"},
}
}
// INSTALLER PACKAGE SET
func installerPackageSet(t *imageType) rpmmd.PackageSet {

View file

@ -2,6 +2,7 @@ package manifest
import (
"github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/platform"
)
// A LiveImgPipeline represents a raw image file which can be booted in a
@ -51,12 +52,12 @@ func (p *LiveImgPipeline) serialize() osbuild2.Pipeline {
pipeline.AddStage(stage)
}
switch p.treePipeline.arch {
case ARCH_S390X:
switch p.treePipeline.platform.GetArch() {
case platform.ARCH_S390X:
loopback := osbuild2.NewLoopbackDevice(&osbuild2.LoopbackDeviceOptions{Filename: p.filename})
pipeline.AddStage(osbuild2.NewZiplInstStage(osbuild2.NewZiplInstStageOptions(p.treePipeline.kernelVer, pt), loopback, copyDevices, copyMounts))
default:
if grubLegacy := p.treePipeline.BIOSPlatform; grubLegacy != "" {
if grubLegacy := p.treePipeline.platform.GetBIOSPlatform(); grubLegacy != "" {
pipeline.AddStage(osbuild2.NewGrub2InstStage(osbuild2.NewGrub2InstStageOption(p.filename, pt, grubLegacy)))
}
}

View file

@ -9,6 +9,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/disk"
"github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/platform"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
@ -49,12 +50,6 @@ type OSPipeline struct {
KernelOptionsAppend []string
// UEFIVendor indicates whether or not the image should support UEFI and
// if set namespaces the UEFI binaries with this string.
UEFIVendor string
// BIOSPlatform indicates whether or not the image should support BIOS
// booting and if set, the name of the platform, e.g., i386-pc
BIOSPlatform string
// GPGKeyFiles are a list of filenames in the OS which will be imported
// as GPG keys into the RPM database.
GPGKeyFiles []string
Language string
Keyboard *string
@ -98,7 +93,7 @@ type OSPipeline struct {
repos []rpmmd.RepoConfig
packageSpecs []rpmmd.PackageSpec
arch Arch
platform platform.Platform
kernelVer string
}
@ -110,12 +105,12 @@ type OSPipeline struct {
// kernel package that will be used on the target system.
func NewOSPipeline(m *Manifest,
buildPipeline *BuildPipeline,
arch Arch,
platform platform.Platform,
repos []rpmmd.RepoConfig) *OSPipeline {
p := &OSPipeline{
BasePipeline: NewBasePipeline(m, "os", buildPipeline, nil),
repos: repos,
arch: arch,
platform: platform,
Language: "C.UTF-8",
Hostname: "localhost.localdomain",
Timezone: "UTC",
@ -127,34 +122,7 @@ func NewOSPipeline(m *Manifest,
}
func (p *OSPipeline) getPackageSetChain() []rpmmd.PackageSet {
packages := []string{}
switch p.arch {
case ARCH_X86_64:
if p.BIOSPlatform != "" {
packages = append(packages,
"dracut-config-generic",
"grub2-pc")
}
if p.UEFIVendor != "" {
packages = append(packages,
"dracut-config-generic",
"efibootmgr",
"grub2-efi-x64",
"shim-x64")
}
case ARCH_AARCH64:
if p.UEFIVendor != "" {
packages = append(packages,
"dracut-config-generic",
"efibootmgr",
"grub2-efi-aa64",
"grub2-tools",
"shim-aa64")
}
default:
panic("unsupported architecture")
}
packages := p.platform.GetPackages()
chain := []rpmmd.PackageSet{
{
@ -175,10 +143,7 @@ func (p *OSPipeline) getPackageSetChain() []rpmmd.PackageSet {
}
func (p *OSPipeline) getBuildPackages() []string {
packages := []string{}
if p.BIOSPlatform != "" {
packages = append(packages, "grub2-pc")
}
packages := p.platform.GetBuildPackages()
if p.OSTree != nil {
packages = append(packages, "rpm-ostree")
}
@ -383,11 +348,15 @@ func (p *OSPipeline) serialize() osbuild2.Pipeline {
pipeline.AddStage(osbuild2.NewFSTabStage(osbuild2.NewFSTabStageOptions(pt)))
var bootloader *osbuild2.Stage
switch p.arch {
case ARCH_S390X:
switch p.platform.GetArch() {
case platform.ARCH_S390X:
bootloader = osbuild2.NewZiplStage(new(osbuild2.ZiplStageOptions))
default:
options := osbuild2.NewGrub2StageOptionsUnified(pt, p.kernelVer, p.UEFIVendor != "", p.BIOSPlatform, p.UEFIVendor, false)
options := osbuild2.NewGrub2StageOptionsUnified(pt,
p.kernelVer,
p.platform.GetUEFIVendor() != "",
p.platform.GetBIOSPlatform(),
p.platform.GetUEFIVendor(), false)
if cfg := p.Grub2Config; cfg != nil {
// TODO: don't store Grub2Config in OSPipeline, making the overrides unnecessary
// grub2.Config.Default is owned and set by `NewGrub2StageOptionsUnified`

View file

@ -0,0 +1,29 @@
package platform
type Aarch64 struct {
BasePlatform
UEFIVendor string
}
func (p *Aarch64) GetArch() Arch {
return ARCH_AARCH64
}
func (p *Aarch64) GetUEFIVendor() string {
return p.UEFIVendor
}
func (p *Aarch64) GetPackages() []string {
packages := p.BasePlatform.FirmwarePackages
if p.UEFIVendor != "" {
packages = append(packages,
"dracut-config-generic",
"efibootmgr",
"grub2-efi-aa64",
"grub2-tools",
"shim-aa64")
}
return packages
}

View file

@ -0,0 +1,58 @@
package platform
type Arch uint64
const (
ARCH_AARCH64 Arch = iota
ARCH_PPC64LE
ARCH_S390X
ARCH_X86_64
)
func (a Arch) String() string {
switch a {
case ARCH_AARCH64:
return "aarch64"
case ARCH_PPC64LE:
return "ppc64le"
case ARCH_S390X:
return "s390x"
case ARCH_X86_64:
return "x86_64"
default:
panic("invalid architecture")
}
}
type Platform interface {
GetArch() Arch
GetBIOSPlatform() string
GetUEFIVendor() string
GetZiplSupport() bool
GetPackages() []string
GetBuildPackages() []string
}
type BasePlatform struct {
FirmwarePackages []string
}
func (p BasePlatform) GetBIOSPlatform() string {
return ""
}
func (p BasePlatform) GetUEFIVendor() string {
return ""
}
func (p BasePlatform) GetZiplSupport() bool {
return false
}
func (p BasePlatform) GetPackages() []string {
return p.FirmwarePackages
}
func (p BasePlatform) GetBuildPackages() []string {
return []string{}
}

View file

@ -0,0 +1,52 @@
package platform
type X86BootLoader uint64
type X86 struct {
BasePlatform
BIOS bool
UEFIVendor string
}
func (p *X86) GetArch() Arch {
return ARCH_X86_64
}
func (p *X86) GetBIOSPlatform() string {
if p.BIOS {
return "i386-pc"
}
return ""
}
func (p *X86) GetUEFIVendor() string {
return p.UEFIVendor
}
func (p *X86) GetPackages() []string {
packages := p.BasePlatform.FirmwarePackages
if p.BIOS {
packages = append(packages,
"dracut-config-generic",
"grub2-pc")
}
if p.UEFIVendor != "" {
packages = append(packages,
"dracut-config-generic",
"efibootmgr",
"grub2-efi-x64",
"shim-x64")
}
return packages
}
func (p *X86) GetBuildPackages() []string {
packages := []string{}
if p.BIOS {
packages = append(packages, "grub2-pc")
}
return packages
}