Skip to content

Commit acb6e9e

Browse files
mvo5supakeen
authored andcommitted
many: add infrastructure for bootc anaconda images
This commit contains the infrastructure in the distro, image and manifest packages to support building bootc based ISO images. This includes: - suppport for setting an installer payload - a new bootc installer image type in pkg/distro/bootc - support for containers in image/anaconda_container_intaller - exported helper to share between bib/images until the legacy ISO rpm installer moved into images too (which is hard) In addition (this was originally a second commit but because the manifest checksum test run per-commit this test update needs to be all one commit): This commit adds reference manifests for the new bootc iso installer image type. It also tweaks `gen-manifests` a bit as we now need to split the tests between using disk and installer image types.
1 parent 9bf04ec commit acb6e9e

File tree

12 files changed

+736
-36
lines changed

12 files changed

+736
-36
lines changed

cmd/gen-manifests/main.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"path/filepath"
1616
"runtime/debug"
17+
"slices"
1718
"strings"
1819
"time"
1920

@@ -611,9 +612,11 @@ func main() {
611612
DefaultFs string `yaml:"default_fs"`
612613
ContainerSize uint64 `yaml:"container_size"`
613614
ImageRef string `yaml:"image_ref"`
615+
ImageTypes []string `yaml:"image_types"`
614616

615-
BuildContainerRef string `yaml:"build_container_ref"`
616-
BuildContainerInfo osinfo.Info `yaml:"build_container_info"`
617+
BuildContainerRef string `yaml:"build_container_ref"`
618+
BuildContainerInfo osinfo.Info `yaml:"build_container_info"`
619+
PayloadContainerRef string `yaml:"payload_container_ref"`
617620
}
618621
type fakeContainersYAML struct {
619622
Containers []fakeBootcContainerYAML
@@ -629,11 +632,6 @@ func main() {
629632
if err != nil {
630633
panic(err)
631634
}
632-
if fakeBootcCnt.BuildContainerRef != "" {
633-
if err := distribution.SetBuildContainerForTesting(fakeBootcCnt.BuildContainerRef, &fakeBootcCnt.BuildContainerInfo); err != nil {
634-
panic(err)
635-
}
636-
}
637635

638636
arches, _ := arches.ResolveArgValues(distribution.ListArches())
639637
for _, archName := range arches {
@@ -643,6 +641,21 @@ func main() {
643641
}
644642
imgTypes, _ := imgTypes.ResolveArgValues(archi.ListImageTypes())
645643
for _, imgTypeName := range imgTypes {
644+
if !slices.Contains(fakeBootcCnt.ImageTypes, imgTypeName) {
645+
continue
646+
}
647+
648+
if fakeBootcCnt.BuildContainerRef != "" {
649+
if err := distribution.SetBuildContainerForTesting(fakeBootcCnt.BuildContainerRef, &fakeBootcCnt.BuildContainerInfo); err != nil {
650+
panic(err)
651+
}
652+
}
653+
if fakeBootcCnt.PayloadContainerRef != "" {
654+
if err := distribution.SetInstallerPayload(fakeBootcCnt.PayloadContainerRef); err != nil {
655+
panic(err)
656+
}
657+
}
658+
646659
imgType, err := archi.GetImageType(imgTypeName)
647660
if err != nil {
648661
panic(err)

pkg/bib/osinfo/osinfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type Info struct {
4040
UEFIVendor string `yaml:"uefi_vendor"`
4141
SELinuxPolicy string `yaml:"selinux_policy"`
4242
ImageCustomization *blueprint.Customizations
43-
KernelInfo *KernelInfo
43+
KernelInfo *KernelInfo `yaml:"kernel_info"`
4444

4545
MountConfiguration *osbuild.MountConfiguration
4646
PartitionTable *disk.PartitionTable

pkg/distro/bootc/bootc.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ import (
3131
var _ = distro.Distro(&BootcDistro{})
3232

3333
type BootcDistro struct {
34-
imgref string
35-
buildImgref string
34+
imgref string
35+
buildImgref string
36+
// XXX: wrong place?
37+
payloadRef string
3638
sourceInfo *osinfo.Info
3739
buildSourceInfo *osinfo.Info
3840

@@ -83,6 +85,12 @@ func (d *BootcDistro) SetBuildContainer(imgref string) (err error) {
8385
return d.setBuildContainer(imgref, info)
8486
}
8587

88+
// XXX: wrong layer
89+
func (d *BootcDistro) SetInstallerPayload(imgref string) error {
90+
d.payloadRef = imgref
91+
return nil
92+
}
93+
8694
func (d *BootcDistro) setBuildContainer(imgref string, info *osinfo.Info) error {
8795
d.buildImgref = imgref
8896
d.buildSourceInfo = info
@@ -312,20 +320,7 @@ func (t *BootcImageType) Manifest(bp *blueprint.Blueprint, options distro.ImageO
312320
//nolint:gosec
313321
rng := rand.New(rand.NewSource(seed))
314322

315-
archi := common.Must(arch.FromString(t.arch.Name()))
316-
platform := &platform.Data{
317-
Arch: archi,
318-
UEFIVendor: t.arch.distro.sourceInfo.UEFIVendor,
319-
QCOW2Compat: "1.1",
320-
}
321-
switch archi {
322-
case arch.ARCH_X86_64:
323-
platform.BIOSPlatform = "i386-pc"
324-
case arch.ARCH_PPC64LE:
325-
platform.BIOSPlatform = "powerpc-ieee1275"
326-
case arch.ARCH_S390X:
327-
platform.ZiplSupport = true
328-
}
323+
platform := PlatformFor(t.arch.Name(), t.arch.distro.sourceInfo.UEFIVendor)
329324
// For the bootc-disk image, the filename is the basename and
330325
// the extension is added automatically for each disk format
331326
filename := strings.Split(t.filename, ".")[0]
@@ -487,6 +482,12 @@ func newBootcDistroAfterIntrospect(archStr string, info *osinfo.Info, imgref, de
487482
filename: "image.ova",
488483
},
489484
)
485+
ba.imageTypes["bootc-installer"] = &BootcAnacondaInstaller{
486+
arch: ba,
487+
name: "bootc-installer",
488+
export: "bootiso",
489+
}
490+
490491
bd.addArches(ba)
491492

492493
return bd, nil

pkg/distro/bootc/iso.go

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package bootc
2+
3+
import (
4+
"fmt"
5+
"math/rand"
6+
7+
"github.com/osbuild/blueprint/pkg/blueprint"
8+
"github.com/osbuild/images/internal/cmdutil"
9+
"github.com/osbuild/images/pkg/arch"
10+
"github.com/osbuild/images/pkg/container"
11+
"github.com/osbuild/images/pkg/customizations/anaconda"
12+
"github.com/osbuild/images/pkg/customizations/kickstart"
13+
"github.com/osbuild/images/pkg/disk"
14+
"github.com/osbuild/images/pkg/distro"
15+
"github.com/osbuild/images/pkg/image"
16+
"github.com/osbuild/images/pkg/manifest"
17+
"github.com/osbuild/images/pkg/osbuild"
18+
"github.com/osbuild/images/pkg/platform"
19+
"github.com/osbuild/images/pkg/rpmmd"
20+
)
21+
22+
var _ = distro.ImageType(&BootcAnacondaInstaller{})
23+
24+
// BootcAnacondaInstaller is an image-type for a bootc
25+
// container based ISO installer.
26+
type BootcAnacondaInstaller struct {
27+
arch *BootcArch
28+
29+
name string
30+
export string
31+
}
32+
33+
func (t *BootcAnacondaInstaller) Name() string {
34+
return t.name
35+
}
36+
37+
func (t *BootcAnacondaInstaller) Aliases() []string {
38+
return nil
39+
}
40+
41+
func (t *BootcAnacondaInstaller) Arch() distro.Arch {
42+
return t.arch
43+
}
44+
45+
func (t *BootcAnacondaInstaller) Filename() string {
46+
return "installer.iso"
47+
}
48+
49+
func (t *BootcAnacondaInstaller) MIMEType() string {
50+
return "application/x-iso9660-image"
51+
}
52+
53+
func (t *BootcAnacondaInstaller) OSTreeRef() string {
54+
return ""
55+
}
56+
57+
func (t *BootcAnacondaInstaller) ISOLabel() (string, error) {
58+
return "Unknown", nil
59+
}
60+
61+
func (t *BootcAnacondaInstaller) Size(size uint64) uint64 {
62+
return size
63+
}
64+
65+
func (t *BootcAnacondaInstaller) PartitionType() disk.PartitionTableType {
66+
return disk.PT_NONE
67+
}
68+
69+
func (t *BootcAnacondaInstaller) BasePartitionTable() (*disk.PartitionTable, error) {
70+
return nil, nil
71+
}
72+
73+
func (t *BootcAnacondaInstaller) BootMode() platform.BootMode {
74+
return platform.BOOT_HYBRID
75+
}
76+
77+
func (t *BootcAnacondaInstaller) BuildPipelines() []string {
78+
return []string{"build"}
79+
}
80+
81+
func (t *BootcAnacondaInstaller) PayloadPipelines() []string {
82+
return []string{""}
83+
}
84+
85+
func (t *BootcAnacondaInstaller) PayloadPackageSets() []string {
86+
return nil
87+
}
88+
89+
func (t *BootcAnacondaInstaller) Exports() []string {
90+
return []string{t.export}
91+
}
92+
93+
func (t *BootcAnacondaInstaller) SupportedBlueprintOptions() []string {
94+
// XXX: this is probably too minimal but lets start small
95+
// and expand
96+
return []string{
97+
"customizations.fips",
98+
"customizations.group",
99+
"customizations.installer",
100+
"customizations.kernel.append",
101+
"customizations.user",
102+
}
103+
}
104+
func (t *BootcAnacondaInstaller) RequiredBlueprintOptions() []string {
105+
return nil
106+
}
107+
108+
// XXX: duplication with BootcImageType
109+
func (t *BootcAnacondaInstaller) Manifest(bp *blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig, seedp *int64) (*manifest.Manifest, []string, error) {
110+
if t.arch.distro.imgref == "" {
111+
return nil, nil, fmt.Errorf("internal error: no base image defined")
112+
}
113+
containerSource := container.SourceSpec{
114+
Source: t.arch.distro.imgref,
115+
Name: t.arch.distro.imgref,
116+
Local: true,
117+
}
118+
// XXX: keep it simple for now, we may allow this in the future
119+
if t.arch.distro.buildImgref != t.arch.distro.imgref {
120+
return nil, nil, fmt.Errorf("cannot use build-containers with anaconda installer images")
121+
}
122+
123+
var customizations *blueprint.Customizations
124+
if bp != nil {
125+
customizations = bp.Customizations
126+
}
127+
seed, err := cmdutil.SeedArgFor(nil, t.Name(), t.arch.Name(), t.arch.distro.Name())
128+
if err != nil {
129+
return nil, nil, err
130+
}
131+
//nolint:gosec
132+
rng := rand.New(rand.NewSource(seed))
133+
134+
platformi := PlatformFor(t.arch.Name(), t.arch.distro.sourceInfo.UEFIVendor)
135+
platformi.ImageFormat = platform.FORMAT_ISO
136+
137+
// XXX: tons of copied code from
138+
// bootc-image-builder:‎bib/cmd/bootc-image-builder/legacy_iso.go
139+
// but sharing is hard because AnacondaContainerInstaller and
140+
// AnacondaContainerInstallerLegacy are different types so
141+
// a shared helper to set the fields won't work (unless
142+
// reflection urgh).
143+
filename := "install.iso"
144+
145+
// The ref is not needed and will be removed from the ctor later
146+
// in time
147+
img := image.NewAnacondaContainerInstaller(platformi, filename, containerSource, "")
148+
img.ContainerRemoveSignatures = true
149+
img.RootfsCompression = "zstd"
150+
// kernelVer is used by dracut
151+
img.KernelVer = t.arch.distro.sourceInfo.KernelInfo.Version
152+
img.KernelPath = fmt.Sprintf("lib/modules/%s/vmlinuz", t.arch.distro.sourceInfo.KernelInfo.Version)
153+
img.InitramfsPath = fmt.Sprintf("lib/modules/%s/initramfs.img", t.arch.distro.sourceInfo.KernelInfo.Version)
154+
img.InstallerHome = "/var/roothome"
155+
payloadSource := container.SourceSpec{
156+
Source: t.arch.distro.payloadRef,
157+
Name: t.arch.distro.payloadRef,
158+
Local: true,
159+
}
160+
img.InstallerPayload = payloadSource
161+
162+
if t.arch.Name() == arch.ARCH_X86_64.String() {
163+
img.InstallerCustomizations.ISOBoot = manifest.Grub2ISOBoot
164+
}
165+
166+
img.InstallerCustomizations.Product = t.arch.distro.sourceInfo.OSRelease.Name
167+
img.InstallerCustomizations.OSVersion = t.arch.distro.sourceInfo.OSRelease.VersionID
168+
img.InstallerCustomizations.ISOLabel = LabelForISO(&t.arch.distro.sourceInfo.OSRelease, t.arch.Name())
169+
170+
img.InstallerCustomizations.FIPS = customizations.GetFIPS()
171+
img.Kickstart, err = kickstart.New(customizations)
172+
if err != nil {
173+
return nil, nil, err
174+
}
175+
img.Kickstart.Path = osbuild.KickstartPathOSBuild
176+
if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" {
177+
img.Kickstart.KernelOptionsAppend = append(img.Kickstart.KernelOptionsAppend, kopts.Append)
178+
}
179+
img.Kickstart.NetworkOnBoot = true
180+
181+
instCust, err := customizations.GetInstaller()
182+
if err != nil {
183+
return nil, nil, err
184+
}
185+
if instCust != nil && instCust.Modules != nil {
186+
img.InstallerCustomizations.EnabledAnacondaModules = append(img.InstallerCustomizations.EnabledAnacondaModules, instCust.Modules.Enable...)
187+
img.InstallerCustomizations.DisabledAnacondaModules = append(img.InstallerCustomizations.DisabledAnacondaModules, instCust.Modules.Disable...)
188+
}
189+
img.InstallerCustomizations.EnabledAnacondaModules = append(img.InstallerCustomizations.EnabledAnacondaModules,
190+
anaconda.ModuleUsers,
191+
anaconda.ModuleServices,
192+
anaconda.ModuleSecurity,
193+
// XXX: get from the imagedefs
194+
anaconda.ModuleNetwork,
195+
anaconda.ModulePayloads,
196+
anaconda.ModuleRuntime,
197+
anaconda.ModuleStorage,
198+
)
199+
200+
img.Kickstart.OSTree = &kickstart.OSTree{
201+
OSName: "default",
202+
}
203+
img.InstallerCustomizations.LoraxTemplates = LoraxTemplates(t.arch.distro.sourceInfo.OSRelease)
204+
img.InstallerCustomizations.LoraxTemplatePackage = LoraxTemplatePackage(t.arch.distro.sourceInfo.OSRelease)
205+
206+
// see https://github.com/osbuild/bootc-image-builder/issues/733
207+
img.InstallerCustomizations.ISORootfsType = manifest.SquashfsRootfs
208+
209+
installRootfsType, err := disk.NewFSType(t.arch.distro.defaultFs)
210+
if err != nil {
211+
return nil, nil, err
212+
}
213+
img.InstallRootfsType = installRootfsType
214+
215+
mf := manifest.New()
216+
217+
foundDistro, foundRunner, err := GetDistroAndRunner(t.arch.distro.sourceInfo.OSRelease)
218+
if err != nil {
219+
return nil, nil, fmt.Errorf("failed to infer distro and runner: %w", err)
220+
}
221+
mf.Distro = foundDistro
222+
223+
_, err = img.InstantiateManifestFromContainer(&mf, []container.SourceSpec{containerSource}, foundRunner, rng)
224+
return &mf, nil, err
225+
}

0 commit comments

Comments
 (0)