Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions cmd/gen-manifests/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"
"path/filepath"
"runtime/debug"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -611,9 +612,11 @@ func main() {
DefaultFs string `yaml:"default_fs"`
ContainerSize uint64 `yaml:"container_size"`
ImageRef string `yaml:"image_ref"`
ImageTypes []string `yaml:"image_types"`

BuildContainerRef string `yaml:"build_container_ref"`
BuildContainerInfo osinfo.Info `yaml:"build_container_info"`
BuildContainerRef string `yaml:"build_container_ref"`
BuildContainerInfo osinfo.Info `yaml:"build_container_info"`
PayloadContainerRef string `yaml:"payload_container_ref"`
}
type fakeContainersYAML struct {
Containers []fakeBootcContainerYAML
Expand All @@ -629,11 +632,6 @@ func main() {
if err != nil {
panic(err)
}
if fakeBootcCnt.BuildContainerRef != "" {
if err := distribution.SetBuildContainerForTesting(fakeBootcCnt.BuildContainerRef, &fakeBootcCnt.BuildContainerInfo); err != nil {
panic(err)
}
}

arches, _ := arches.ResolveArgValues(distribution.ListArches())
for _, archName := range arches {
Expand All @@ -643,6 +641,16 @@ func main() {
}
imgTypes, _ := imgTypes.ResolveArgValues(archi.ListImageTypes())
for _, imgTypeName := range imgTypes {
if !slices.Contains(fakeBootcCnt.ImageTypes, imgTypeName) {
continue
}

if fakeBootcCnt.BuildContainerRef != "" {
if err := distribution.SetBuildContainerForTesting(fakeBootcCnt.BuildContainerRef, &fakeBootcCnt.BuildContainerInfo); err != nil {
panic(err)
}
}

imgType, err := archi.GetImageType(imgTypeName)
if err != nil {
panic(err)
Expand All @@ -660,6 +668,11 @@ func main() {
fmt.Printf("Skipping %s for %s/%s (reason: %v)\n", itConfig.Name, imgTypeName, distribution.Name(), reason)
continue
}
if fakeBootcCnt.PayloadContainerRef != "" {
itConfig.Options.Bootc = &distro.BootcImageOptions{
InstallerPayloadRef: fakeBootcCnt.PayloadContainerRef,
}
}

var repos []rpmmd.RepoConfig
job := makeManifestJob(itConfig, imgType, distribution, repos, archName, cacheRoot, outputDir, contentResolve, metadata, tmpdirRoot)
Expand Down
2 changes: 1 addition & 1 deletion pkg/bib/osinfo/osinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Info struct {
UEFIVendor string `yaml:"uefi_vendor"`
SELinuxPolicy string `yaml:"selinux_policy"`
ImageCustomization *blueprint.Customizations
KernelInfo *KernelInfo
KernelInfo *KernelInfo `yaml:"kernel_info"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems unrelated to the commit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we split this into its own commit?


MountConfiguration *osbuild.MountConfiguration
PartitionTable *disk.PartitionTable
Expand Down
21 changes: 7 additions & 14 deletions pkg/distro/bootc/bootc.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,7 @@ func (t *BootcImageType) Manifest(bp *blueprint.Blueprint, options distro.ImageO
//nolint:gosec
rng := rand.New(rand.NewSource(seed))

archi := common.Must(arch.FromString(t.arch.Name()))
platform := &platform.Data{
Arch: archi,
UEFIVendor: t.arch.distro.sourceInfo.UEFIVendor,
QCOW2Compat: "1.1",
}
switch archi {
case arch.ARCH_X86_64:
platform.BIOSPlatform = "i386-pc"
case arch.ARCH_PPC64LE:
platform.BIOSPlatform = "powerpc-ieee1275"
case arch.ARCH_S390X:
platform.ZiplSupport = true
}
platform := PlatformFor(t.arch.Name(), t.arch.distro.sourceInfo.UEFIVendor)
// For the bootc-disk image, the filename is the basename and
// the extension is added automatically for each disk format
filename := strings.Split(t.filename, ".")[0]
Expand Down Expand Up @@ -487,6 +474,12 @@ func newBootcDistroAfterIntrospect(archStr string, info *osinfo.Info, imgref, de
filename: "image.ova",
},
)
ba.imageTypes["bootc-installer"] = &BootcAnacondaInstaller{
arch: ba,
name: "bootc-installer",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideas for a better name welcome! It could be just installer even as its only available in the bootc distro (but not anaconda-iso, iso as this is taken by bootc-image-builder allready)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bootc-iso?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually like to be clearer and have anaconda in the name; in case we ever want to have other installers. anaconda-iso is what we use in bootc-image-builder (which has an iso alias for backwards compatibility). Maybe bootc-anaconda-iso?

@supakeen supakeen Oct 10, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is it not required that the ISO container contains Anaconda?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think will almost certainly not work if its not an anaconda image, our pipeline has a bunch of expectations around this so I wouldn't mind adding anaconda in the same.

export: "bootiso",
}

bd.addArches(ba)

return bd, nil
Expand Down
233 changes: 233 additions & 0 deletions pkg/distro/bootc/iso.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package bootc

import (
"fmt"
"math/rand"

"github.com/osbuild/blueprint/pkg/blueprint"
"github.com/osbuild/images/internal/cmdutil"
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/anaconda"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/image"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rpmmd"
)

var _ = distro.ImageType(&BootcAnacondaInstaller{})

// BootcAnacondaInstaller is an image-type for a bootc
// container based ISO installer.
type BootcAnacondaInstaller struct {
arch *BootcArch

name string
export string
}

func (t *BootcAnacondaInstaller) Name() string {
return t.name
}

func (t *BootcAnacondaInstaller) Aliases() []string {
return nil
}

func (t *BootcAnacondaInstaller) Arch() distro.Arch {
return t.arch
}

func (t *BootcAnacondaInstaller) Filename() string {
return "installer.iso"
}

func (t *BootcAnacondaInstaller) MIMEType() string {
return "application/x-iso9660-image"
}

func (t *BootcAnacondaInstaller) OSTreeRef() string {
return ""
}

func (t *BootcAnacondaInstaller) ISOLabel() (string, error) {
return "Unknown", nil
}

func (t *BootcAnacondaInstaller) Size(size uint64) uint64 {
return size
}

func (t *BootcAnacondaInstaller) PartitionType() disk.PartitionTableType {
return disk.PT_NONE
}

func (t *BootcAnacondaInstaller) BasePartitionTable() (*disk.PartitionTable, error) {
return nil, nil
}

func (t *BootcAnacondaInstaller) BootMode() platform.BootMode {
return platform.BOOT_HYBRID
}

func (t *BootcAnacondaInstaller) BuildPipelines() []string {
return []string{"build"}
}

func (t *BootcAnacondaInstaller) PayloadPipelines() []string {
return []string{""}
}

func (t *BootcAnacondaInstaller) PayloadPackageSets() []string {
return nil
}

func (t *BootcAnacondaInstaller) Exports() []string {
return []string{t.export}
}

func (t *BootcAnacondaInstaller) SupportedBlueprintOptions() []string {
// XXX: this is probably too minimal but lets start small
// and expand
return []string{
"customizations.fips",
"customizations.group",
"customizations.installer",
"customizations.kernel.append",
"customizations.user",
}
}
func (t *BootcAnacondaInstaller) RequiredBlueprintOptions() []string {
return nil
}

// XXX: duplication with BootcImageType
func (t *BootcAnacondaInstaller) Manifest(bp *blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig, seedp *int64) (*manifest.Manifest, []string, error) {
if t.arch.distro.imgref == "" {
return nil, nil, fmt.Errorf("internal error: no base image defined")
}
if options.Bootc == nil || options.Bootc.InstallerPayloadRef == "" {
return nil, nil, fmt.Errorf("no installer payload bootc ref set")
}
payloadRef := options.Bootc.InstallerPayloadRef

containerSource := container.SourceSpec{
Source: t.arch.distro.imgref,
Name: t.arch.distro.imgref,
Local: true,
}
// XXX: keep it simple for now, we may allow this in the future
if t.arch.distro.buildImgref != t.arch.distro.imgref {
return nil, nil, fmt.Errorf("cannot use build-containers with anaconda installer images")
}

var customizations *blueprint.Customizations
if bp != nil {
customizations = bp.Customizations
}
seed, err := cmdutil.SeedArgFor(nil, t.Name(), t.arch.Name(), t.arch.distro.Name())
if err != nil {
return nil, nil, err
}
//nolint:gosec
rng := rand.New(rand.NewSource(seed))

platformi := PlatformFor(t.arch.Name(), t.arch.distro.sourceInfo.UEFIVendor)
platformi.ImageFormat = platform.FORMAT_ISO

// XXX: tons of copied code from
// bootc-image-builder:‎bib/cmd/bootc-image-builder/legacy_iso.go
// but sharing is hard because AnacondaContainerInstaller and
// AnacondaContainerInstallerLegacy are different types so
// a shared helper to set the fields won't work (unless
// reflection urgh).
filename := "install.iso"

// The ref is not needed and will be removed from the ctor later
// in time
img := image.NewAnacondaContainerInstaller(platformi, filename, containerSource, "")
img.ContainerRemoveSignatures = true
img.RootfsCompression = "zstd"
// kernelVer is used by dracut
img.KernelVer = t.arch.distro.sourceInfo.KernelInfo.Version
img.KernelPath = fmt.Sprintf("lib/modules/%s/vmlinuz", t.arch.distro.sourceInfo.KernelInfo.Version)
img.InitramfsPath = fmt.Sprintf("lib/modules/%s/initramfs.img", t.arch.distro.sourceInfo.KernelInfo.Version)
img.InstallerHome = "/var/roothome"
payloadSource := container.SourceSpec{
Source: payloadRef,
Name: payloadRef,
Local: true,
}
img.InstallerPayload = payloadSource

if t.arch.Name() == arch.ARCH_X86_64.String() {
img.InstallerCustomizations.ISOBoot = manifest.Grub2ISOBoot
}

img.InstallerCustomizations.Product = t.arch.distro.sourceInfo.OSRelease.Name
img.InstallerCustomizations.OSVersion = t.arch.distro.sourceInfo.OSRelease.VersionID
img.InstallerCustomizations.ISOLabel = LabelForISO(&t.arch.distro.sourceInfo.OSRelease, t.arch.Name())

img.InstallerCustomizations.FIPS = customizations.GetFIPS()
img.Kickstart, err = kickstart.New(customizations)
if err != nil {
return nil, nil, err
}
img.Kickstart.Path = osbuild.KickstartPathOSBuild
if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" {
img.Kickstart.KernelOptionsAppend = append(img.Kickstart.KernelOptionsAppend, kopts.Append)
}
img.Kickstart.NetworkOnBoot = true

instCust, err := customizations.GetInstaller()
if err != nil {
return nil, nil, err
}
if instCust != nil && instCust.Modules != nil {
img.InstallerCustomizations.EnabledAnacondaModules = append(img.InstallerCustomizations.EnabledAnacondaModules, instCust.Modules.Enable...)
img.InstallerCustomizations.DisabledAnacondaModules = append(img.InstallerCustomizations.DisabledAnacondaModules, instCust.Modules.Disable...)
}
img.InstallerCustomizations.EnabledAnacondaModules = append(img.InstallerCustomizations.EnabledAnacondaModules,
anaconda.ModuleUsers,
anaconda.ModuleServices,
anaconda.ModuleSecurity,
// XXX: get from the imagedefs
anaconda.ModuleNetwork,
anaconda.ModulePayloads,
anaconda.ModuleRuntime,
anaconda.ModuleStorage,
)
if bpKernel := customizations.GetKernel(); bpKernel.Append != "" {
img.InstallerCustomizations.KernelOptionsAppend = append(img.InstallerCustomizations.KernelOptionsAppend, bpKernel.Append)
}

img.Kickstart.OSTree = &kickstart.OSTree{
OSName: "default",
}
img.InstallerCustomizations.LoraxTemplates = LoraxTemplates(t.arch.distro.sourceInfo.OSRelease)
img.InstallerCustomizations.LoraxTemplatePackage = LoraxTemplatePackage(t.arch.distro.sourceInfo.OSRelease)

// see https://github.com/osbuild/bootc-image-builder/issues/733
img.InstallerCustomizations.ISORootfsType = manifest.SquashfsRootfs

installRootfsType, err := disk.NewFSType(t.arch.distro.defaultFs)
if err != nil {
return nil, nil, err
}
img.InstallRootfsType = installRootfsType

mf := manifest.New()

foundDistro, foundRunner, err := GetDistroAndRunner(t.arch.distro.sourceInfo.OSRelease)
if err != nil {
return nil, nil, fmt.Errorf("failed to infer distro and runner: %w", err)
}
mf.Distro = foundDistro

_, err = img.InstantiateManifestFromContainer(&mf, []container.SourceSpec{containerSource}, foundRunner, rng)
return &mf, nil, err
}
Loading
Loading