diff --git a/cmd/gen-manifests/main.go b/cmd/gen-manifests/main.go index 59496b1ea9..5ce065428e 100644 --- a/cmd/gen-manifests/main.go +++ b/cmd/gen-manifests/main.go @@ -23,6 +23,7 @@ import ( "github.com/osbuild/images/pkg/blueprint" "github.com/osbuild/images/pkg/container" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/generic" "github.com/osbuild/images/pkg/distrofactory" "github.com/osbuild/images/pkg/dnfjson" "github.com/osbuild/images/pkg/experimentalflags" @@ -248,7 +249,6 @@ func makeManifestJob( options.Facts = &facts.ImageOptions{ APIType: facts.TEST_APITYPE, } - job := func(msgq chan string) (err error) { defer func() { msg := fmt.Sprintf("Finished job %s", filename) @@ -400,11 +400,13 @@ func main() { flag.BoolVar(&commits, "commits", false, "resolve ostree commit IDs") // manifest selection args - var arches, distros, imgTypes cmdutil.MultiValue + var arches, distros, imgTypes, bootcRefs cmdutil.MultiValue flag.Var(&arches, "arches", "comma-separated list of architectures (globs supported)") flag.Var(&distros, "distros", "comma-separated list of distributions (globs supported)") flag.Var(&imgTypes, "types", "comma-separated list of image types (globs supported)") + flag.Var(&bootcRefs, "bootc-refs", "comma-separated list of bootc-refs") + flag.Parse() testedRepoRegistry, err := testrepos.New() @@ -496,7 +498,7 @@ func main() { if skipNoconfig { continue } - panic(fmt.Sprintf("no configs defined for image type %q for %s", imgTypeName, distribution.Name())) + panic(fmt.Sprintf("no configs defined for image type %q for %s/%s", imgTypeName, distribution.Name(), archName)) } for _, itConfig := range imgTypeConfigs { @@ -511,6 +513,36 @@ func main() { } } } + for _, bootcRef := range bootcRefs { + for _, archName := range arches { + for _, imgTypeName := range imgTypes { + imgType, err := generic.ImageFromBootc(bootcRef, imgTypeName, archName) + if err != nil { + panic(err) + } + distribution := imgType.Arch().Distro() + + // XXX: copied from loop above + imgTypeConfigs := configs.Get(distribution.Name(), archName, imgTypeName) + if len(imgTypeConfigs) == 0 { + if skipNoconfig { + continue + } + panic(fmt.Sprintf("no configs defined for image type %q for %s", imgTypeName, distribution.Name())) + } + for _, itConfig := range imgTypeConfigs { + if needsSkipping, reason := configs.needsSkipping(distribution.Name(), itConfig); needsSkipping { + fmt.Printf("Skipping %s for %s/%s (reason: %v)\n", itConfig.Name, imgTypeName, distribution.Name(), reason) + continue + } + + var repos []rpmmd.RepoConfig + job := makeManifestJob(itConfig, imgType, distribution, repos, archName, cacheRoot, outputDir, contentResolve, metadata) + jobs = append(jobs, job) + } + } + } + } nJobs := len(jobs) fmt.Printf("Collected %d jobs\n", nJobs) diff --git a/pkg/disk/btrfs.go b/pkg/disk/btrfs.go index b4ffb31963..2b360b39cc 100644 --- a/pkg/disk/btrfs.go +++ b/pkg/disk/btrfs.go @@ -19,6 +19,8 @@ type Btrfs struct { Subvolumes []BtrfsSubvolume `json:"subvolumes,omitempty" yaml:"subvolumes,omitempty"` } +var _ = MountpointCreator(&Btrfs{}) + func init() { payloadEntityMap["btrfs"] = reflect.TypeOf(Btrfs{}) } @@ -58,7 +60,7 @@ func (b *Btrfs) GetItemCount() uint { func (b *Btrfs) GetChild(n uint) Entity { return &b.Subvolumes[n] } -func (b *Btrfs) CreateMountpoint(mountpoint string, size uint64) (Entity, error) { +func (b *Btrfs) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) { name := mountpoint if name == "/" { name = "root" diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index 9bce545504..d0c84f3809 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -374,7 +374,7 @@ type FSTabEntity interface { // CreateMountpoint creates a new mountpoint with the given size and // returns the entity that represents the new mountpoint. type MountpointCreator interface { - CreateMountpoint(mountpoint string, size uint64) (Entity, error) + CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) // AlignUp will align the given bytes according to the // requirements of the container type. diff --git a/pkg/disk/disk_test.go b/pkg/disk/disk_test.go index 4785f138ec..7a9ef8ae36 100644 --- a/pkg/disk/disk_test.go +++ b/pkg/disk/disk_test.go @@ -80,7 +80,7 @@ func TestDynamicallyResizePartitionTable(t *testing.T) { // math/rand is good enough in this case /* #nosec G404 */ rng := rand.New(rand.NewSource(0)) - newpt, err := disk.NewPartitionTable(&pt, mountpoints, 1024, disk.RawPartitioningMode, arch.ARCH_AARCH64, nil, rng) + newpt, err := disk.NewPartitionTable(&pt, mountpoints, 1024, disk.RawPartitioningMode, arch.ARCH_AARCH64, nil, "", rng) assert.NoError(t, err) assert.GreaterOrEqual(t, newpt.Size, expectedSize) } @@ -193,7 +193,7 @@ func TestCreatePartitionTable(t *testing.T) { if ptName == "luks+lvm" { ptMode = disk.AutoLVMPartitioningMode } - mpt, err := disk.NewPartitionTable(&pt, bp, uint64(13*MiB), ptMode, arch.ARCH_PPC64LE, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, bp, uint64(13*MiB), ptMode, arch.ARCH_PPC64LE, nil, "", rng) require.NoError(t, err, "Partition table generation failed: PT %q BP %q (%s)", ptName, bpName, err) assert.NotNil(mpt, "Partition table generation failed: PT %q BP %q (nil partition table)", ptName, bpName) assert.Greater(mpt.GetSize(), sumSizes(bp)) @@ -217,12 +217,12 @@ func TestCreatePartitionTableLVMify(t *testing.T) { for bpName, tbp := range testBlueprints { for ptName, pt := range testdisk.TestPartitionTables() { if tbp != nil && (ptName == "btrfs" || ptName == "luks") { - _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng) + _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.Error(err, "PT %q BP %q: should return an error with LVMPartitioningMode", ptName, bpName) continue } - mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.NoError(err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err) rootPath := disk.EntityPath(mpt, "/") @@ -253,12 +253,12 @@ func TestCreatePartitionTableBtrfsify(t *testing.T) { for bpName, tbp := range testBlueprints { for ptName, pt := range testdisk.TestPartitionTables() { if ptName == "auto-lvm" || ptName == "luks" || ptName == "luks+lvm" { - _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, rng) + _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.Error(err, "PT %q BP %q: should return an error with BtrfsPartitioningMode", ptName, bpName) continue } - mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.BtrfsPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.NoError(err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err) rootPath := disk.EntityPath(mpt, "/") @@ -289,12 +289,12 @@ func TestCreatePartitionTableLVMOnly(t *testing.T) { for bpName, tbp := range testBlueprints { for ptName, pt := range testdisk.TestPartitionTables() { if ptName == "btrfs" || ptName == "luks" { - _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.LVMPartitioningMode, arch.ARCH_S390X, nil, rng) + _, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.LVMPartitioningMode, arch.ARCH_S390X, nil, "", rng) assert.Error(err, "PT %q BP %q: should return an error with LVMPartitioningMode", ptName, bpName) continue } - mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.LVMPartitioningMode, arch.ARCH_S390X, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tbp, uint64(13*MiB), disk.LVMPartitioningMode, arch.ARCH_S390X, nil, "", rng) require.NoError(t, err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err) rootPath := disk.EntityPath(mpt, "/") @@ -446,7 +446,7 @@ func TestMinimumSizes(t *testing.T) { for idx, tc := range testCases { { // without LVM - mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.RawPartitioningMode, arch.ARCH_X86_64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.RawPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.NoError(err) for mnt, minSize := range tc.ExpectedMinSizes { path := disk.EntityPath(mpt, mnt) @@ -460,7 +460,7 @@ func TestMinimumSizes(t *testing.T) { } { // with LVM - mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.NoError(err) for mnt, minSize := range tc.ExpectedMinSizes { path := disk.EntityPath(mpt, mnt) @@ -547,7 +547,7 @@ func TestLVMExtentAlignment(t *testing.T) { } for idx, tc := range testCases { - mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, nil, "", rng) assert.NoError(err) for mnt, expSize := range tc.ExpectedSizes { path := disk.EntityPath(mpt, mnt) @@ -576,7 +576,7 @@ func TestNewBootWithSizeLVMify(t *testing.T) { }, } - mpt, err := disk.NewPartitionTable(&pt, custom, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, nil, rng) + mpt, err := disk.NewPartitionTable(&pt, custom, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, nil, "", rng) assert.NoError(err) for idx, c := range custom { @@ -919,7 +919,7 @@ func TestMinimumSizesWithRequiredSizes(t *testing.T) { for idx, tc := range testCases { { // without LVM - mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.RawPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng) + mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.RawPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, "", rng) assert.NoError(err) for mnt, minSize := range tc.ExpectedMinSizes { path := disk.EntityPath(mpt, mnt) @@ -933,7 +933,7 @@ func TestMinimumSizesWithRequiredSizes(t *testing.T) { } { // with LVM - mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng) + mpt, err := disk.NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, "", rng) assert.NoError(err) for mnt, minSize := range tc.ExpectedMinSizes { path := disk.EntityPath(mpt, mnt) diff --git a/pkg/disk/lvm.go b/pkg/disk/lvm.go index 0a4035244d..0578ee8a6c 100644 --- a/pkg/disk/lvm.go +++ b/pkg/disk/lvm.go @@ -21,6 +21,8 @@ type LVMVolumeGroup struct { LogicalVolumes []LVMLogicalVolume `json:"logical_volumes,omitempty" yaml:"logical_volumes,omitempty"` } +var _ = MountpointCreator(&LVMVolumeGroup{}) + func init() { payloadEntityMap["lvm"] = reflect.TypeOf(LVMVolumeGroup{}) } @@ -73,10 +75,13 @@ func (vg *LVMVolumeGroup) GetChild(n uint) Entity { return &vg.LogicalVolumes[n] } -func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint string, size uint64) (Entity, error) { +func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) { + if defaultFs == "" { + defaultFs = "xfs" + } filesystem := Filesystem{ - Type: "xfs", + Type: defaultFs, Mountpoint: mountpoint, FSTabOptions: "defaults", FSTabFreq: 0, diff --git a/pkg/disk/lvm_test.go b/pkg/disk/lvm_test.go index 8b0c3f419f..e52e9eb753 100644 --- a/pkg/disk/lvm_test.go +++ b/pkg/disk/lvm_test.go @@ -19,15 +19,15 @@ func TestLVMVCreateMountpoint(t *testing.T) { Description: "root volume group", } - entity, err := vg.CreateMountpoint("/", 0) + entity, err := vg.CreateMountpoint("/", "", 0) assert.NoError(err) rootlv := entity.(*LVMLogicalVolume) assert.Equal("rootlv", rootlv.Name) - _, err = vg.CreateMountpoint("/home_test", 0) + _, err = vg.CreateMountpoint("/home_test", "", 0) assert.NoError(err) - entity, err = vg.CreateMountpoint("/home/test", 0) + entity, err = vg.CreateMountpoint("/home/test", "", 0) assert.NoError(err) dedup := entity.(*LVMLogicalVolume) @@ -35,11 +35,11 @@ func TestLVMVCreateMountpoint(t *testing.T) { // Lets collide it for i := 0; i < 99; i++ { - _, err = vg.CreateMountpoint("/home/test", 0) + _, err = vg.CreateMountpoint("/home/test", "", 0) assert.NoError(err) } - _, err = vg.CreateMountpoint("/home/test", 0) + _, err = vg.CreateMountpoint("/home/test", "", 0) assert.Error(err) } diff --git a/pkg/disk/partition_table.go b/pkg/disk/partition_table.go index 0130fb3104..f14db22567 100644 --- a/pkg/disk/partition_table.go +++ b/pkg/disk/partition_table.go @@ -31,6 +31,8 @@ type PartitionTable struct { StartOffset uint64 `json:"start_offset,omitempty" yaml:"start_offset,omitempty"` } +var _ = MountpointCreator(&PartitionTable{}) + type PartitioningMode string const ( @@ -110,7 +112,7 @@ const DefaultBootPartitionSize = 1 * datasizes.GiB // containing the root filesystem is grown to fill any left over space on the // partition table. Logical Volumes are not grown to fill the space in the // Volume Group since they are trivial to grow on a live system. -func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, mode PartitioningMode, architecture arch.Arch, requiredSizes map[string]uint64, rng *rand.Rand) (*PartitionTable, error) { +func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, mode PartitioningMode, architecture arch.Arch, requiredSizes map[string]uint64, defaultFs string, rng *rand.Rand) (*PartitionTable, error) { newPT := basePT.Clone().(*PartitionTable) if basePT.features().LVM && (mode == RawPartitioningMode || mode == BtrfsPartitioningMode) { @@ -118,7 +120,7 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste } // first pass: enlarge existing mountpoints and collect new ones - newMountpoints, _ := newPT.applyCustomization(mountpoints, false) + newMountpoints, _ := newPT.applyCustomization(mountpoints, defaultFs, false) var ensureLVM, ensureBtrfs bool switch mode { @@ -147,7 +149,7 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste // second pass: deal with new mountpoints and newly created ones, after switching to // the LVM layout, if requested, which might introduce new mount points, i.e. `/boot` - _, err := newPT.applyCustomization(newMountpoints, true) + _, err := newPT.applyCustomization(newMountpoints, defaultFs, true) if err != nil { return nil, err } @@ -356,9 +358,12 @@ func (pt *PartitionTable) EnsureDirectorySizes(dirSizeMap map[string]uint64) { } } -func (pt *PartitionTable) CreateMountpoint(mountpoint string, size uint64) (Entity, error) { +func (pt *PartitionTable) CreateMountpoint(mountpoint, defaultFs string, size uint64) (Entity, error) { + if defaultFs == "" { + defaultFs = "xfs" + } filesystem := Filesystem{ - Type: "xfs", + Type: defaultFs, Mountpoint: mountpoint, FSTabOptions: "defaults", FSTabFreq: 0, @@ -457,7 +462,7 @@ func (pt *PartitionTable) HeaderSize() uint64 { // return a list of left-over mountpoints (i.e. mountpoints in the input that // were not created). An error can only occur if create is set. // Does not relayout the table, i.e. a call to relayout might be needed. -func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemCustomization, create bool) ([]blueprint.FilesystemCustomization, error) { +func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemCustomization, defaultFs string, create bool) ([]blueprint.FilesystemCustomization, error) { newMountpoints := []blueprint.FilesystemCustomization{} @@ -469,7 +474,7 @@ func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemC } else { if !create { newMountpoints = append(newMountpoints, mnt) - } else if err := pt.createFilesystem(mnt.Mountpoint, size); err != nil { + } else if err := pt.createFilesystem(mnt.Mountpoint, defaultFs, size); err != nil { return nil, err } } @@ -543,7 +548,7 @@ func (pt *PartitionTable) relayout(size uint64) uint64 { return start } -func (pt *PartitionTable) createFilesystem(mountpoint string, size uint64) error { +func (pt *PartitionTable) createFilesystem(mountpoint, defaultFs string, size uint64) error { rootPath := entityPath(pt, "/") if rootPath == nil { panic("no root mountpoint for PartitionTable") @@ -563,7 +568,7 @@ func (pt *PartitionTable) createFilesystem(mountpoint string, size uint64) error panic("could not find root volume container") } - newVol, err := vc.CreateMountpoint(mountpoint, 0) + newVol, err := vc.CreateMountpoint(mountpoint, defaultFs, 0) if err != nil { return fmt.Errorf("failed creating volume: %w", err) } @@ -752,7 +757,7 @@ func (pt *PartitionTable) ensureLVM() error { // we need a /boot partition to boot LVM, ensure one exists bootPath := entityPath(pt, "/boot") if bootPath == nil { - _, err := pt.CreateMountpoint("/boot", DefaultBootPartitionSize) + _, err := pt.CreateMountpoint("/boot", "", DefaultBootPartitionSize) if err != nil { return err @@ -811,13 +816,22 @@ func (pt *PartitionTable) ensureBtrfs(architecture arch.Arch) error { // we need a /boot partition to boot btrfs, ensure one exists bootPath := entityPath(pt, "/boot") if bootPath == nil { - _, err := pt.CreateMountpoint("/boot", DefaultBootPartitionSize) + _, err := pt.CreateMountpoint("/boot", "", DefaultBootPartitionSize) if err != nil { return fmt.Errorf("failed to create /boot partition when ensuring btrfs: %w", err) } rootPath = entityPath(pt, "/") } + // ensure /boot is not set to "btrfs", this can happen on e.g. bootc partition tables that + // do not declare a default fs + bootFilesystem, ok := bootPath[0].(*Filesystem) + if !ok { + return fmt.Errorf("internal error: boot entity not a filesystem: %T", bootPath[0]) + } + if bootFilesystem.Type == "btrfs" { + bootFilesystem.Type = "ext4" + } parent := rootPath[1] // NB: entityPath has reversed order diff --git a/pkg/distro/defs/bootc/distro.yaml b/pkg/distro/defs/bootc/distro.yaml new file mode 100644 index 0000000000..1bf6739198 --- /dev/null +++ b/pkg/distro/defs/bootc/distro.yaml @@ -0,0 +1,532 @@ +.common: + kernel_options: + bootc_kernel_options: &bootc_kernel_options + - rw + - console=tty0 + - console=ttyS0 + + disk_sizes: + default_required_partition_sizes: &default_required_dir_sizes + "/": 1_073_741_824 # 1 * datasizes.GiB + "/usr": 2_147_483_648 # 2 * datasizes.GiB + partitioning: + ids: + - &prep_partition_dosid "41" + - &filesystem_linux_dosid "83" + - &fat16_bdosid "06" + guids: + - &bios_boot_partition_guid "21686148-6449-6E6F-744E-656564454649" + - &efi_system_partition_guid "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" + - &filesystem_data_guid "0FC63DAF-8483-4772-8E79-3D69D8477DE4" + - &xboot_ldr_partition_guid "BC13C2FF-59E6-4262-A352-B275FD6F7172" + - &prep_partition_guid "9E1A2D38-C612-4316-AA26-8B49521E5A8B" + # static UUIDs for partitions and filesystems + # NOTE(akoutsou): These are unnecessary and have stuck around since the + # beginning where (I believe) the goal was to have predictable, + # reproducible partition tables. They might be removed soon in favour of + # proper, random UUIDs, with reproducibility being controlled by fixing + # rng seeds. + uuids: + - &bios_boot_partition_uuid "FAC7F1FB-3E8D-4137-A512-961DE09A5549" + - &root_partition_uuid "6264D520-3FB9-423F-8AB8-7A0A8E3D3562" + - &data_partition_uuid "CB07C243-BC44-4717-853E-28852021225B" + - &efi_system_partition_uuid "68B2905B-DF3E-4FB3-80FA-49D1E773AA33" + - &efi_filesystem_uuid "7B77-95E7" + + platforms: &default_bootc_platforms + - arch: "x86_64" + bios_platform: "i386-pc" + uefi_vendor: "{{.DistroVendor}}" + - arch: "aarch64" + uefi_vendor: "{{.DistroVendor}}" + qcow2_compat: "1.1" + - arch: "s390x" + qcow2_compat: "1.1" + zipl_support: true + - arch: "ppc64le" + bios_platform: "powerpc-ieee1275" + qcow2_compat: "1.1" + + partition_tables: + default_bootc_partition_table: &default_bootc_partition_table + x86_64: + uuid: "D209C89E-EA5E-4FBD-B161-B461CCE297E0" + type: "gpt" + partitions: + - &default_partition_table_part_bios + size: "1 MiB" + bootable: true + type: *bios_boot_partition_guid + uuid: *bios_boot_partition_uuid + - &default_partition_table_part_efi + size: "501 MiB" + type: *efi_system_partition_guid + uuid: *efi_system_partition_uuid + payload_type: "filesystem" + payload: + type: vfat + uuid: *efi_filesystem_uuid + mountpoint: "/boot/efi" + label: "EFI-SYSTEM" + fstab_options: "umask=0077,shortname=winnt" + fstab_freq: 0 + fstab_passno: 2 + - &default_partition_table_part_boot + size: "1 GiB" + type: *filesystem_data_guid + uuid: *data_partition_uuid + payload_type: "filesystem" + payload: + type: distro-default + mountpoint: "/boot" + label: "boot" + fstab_options: "ro" + - &default_partition_table_part_root + size: "2 GiB" + type: *filesystem_data_guid + uuid: *root_partition_uuid + payload_type: "filesystem" + payload: &default_partition_table_part_root_payload + type: distro-default + label: "root" + mountpoint: "/" + fstab_options: "ro" + aarch64: &default_partition_table_aarch64 + uuid: "D209C89E-EA5E-4FBD-B161-B461CCE297E0" + type: "gpt" + partitions: + - *default_partition_table_part_efi + - *default_partition_table_part_boot + - *default_partition_table_part_root + ppc64le: + uuid: "D209C89E-EA5E-4FBD-B161-B461CCE297E0" + type: "gpt" + partitions: + - size: "4 MiB" + bootable: true + type: *prep_partition_guid + - *default_partition_table_part_boot + - *default_partition_table_part_root + s390x: + uuid: "D209C89E-EA5E-4FBD-B161-B461CCE297E0" + type: "gpt" + partitions: + - *default_partition_table_part_boot + - *default_partition_table_part_root + +image_types: + "raw": &raw_image_type + # XXX: the filenames are all hardcoded in the bootcDisk image + filename: "disk.raw" + image_func: "bootc_disk" + mime_type: "application/octet-stream" + bootable: true + default_size: 10_737_418_240 # 10 GiB + payload_pipelines: ["os", "image"] + exports: ["image"] + required_partition_sizes: *default_required_dir_sizes + platforms: *default_bootc_platforms + partition_table: *default_bootc_partition_table + image_config: + kernel_options: *bootc_kernel_options + + "ami": + <<: *raw_image_type + + "qcow2": + <<: *raw_image_type + mime_type: "application/x-qemu-disk" + build_pipelines: ["build"] + payload_pipelines: ["image", "qcow2"] + exports: ["qcow2"] + + "vmdk": + <<: *raw_image_type + mime_type: "application/x-vmdk" + payload_pipelines: ["os", "image", "vmdk"] + exports: ["vmdk"] + + "vhd": + <<: *raw_image_type + mime_type: "application/x-vhd" + payload_pipelines: ["os", "image", "vpc"] + exports: ["vpc"] + + "gce": + <<: *raw_image_type + mime_type: "application/gzip" + payload_pipelines: ["os", "image", "gce"] + exports: ["gce"] + + "anaconda-iso": + filename: "installer.iso" + mime_type: "application/x-iso9660-image" + bootable: true + boot_iso: true + # XXX: not part of the bib go code + default_size: 10_737_418_240 # 10 * datasizes.GibiByte + image_func: "bootc_installer" + build_pipelines: ["build"] + payload_pipelines: + - "ostree-deployment" + - "image" + # this should be zstd + - "xz" + - "coi-tree" + - "efiboot-tree" + - "bootiso-tree" + - "bootiso" + exports: ["bootiso"] + platforms: + # no iso support for ppc64le/s390x yet + - arch: "x86_64" + bios_platform: "i386-pc" + - arch: "aarch64" + uefi_vendor: "fedora" + qcow2_compat: "1.1" + partition_table: *default_bootc_partition_table + package_sets: + installer: + - conditions: + "rhel-10": + when: + distro_name: "bootc-rhel" + version_greater_or_equal: "10" + append: &pkgset_rhel10 + include: + - "@hardware-support" + - alsa-firmware + - alsa-tools-firmware + - anaconda + - anaconda-dracut + - anaconda-install-img-deps + - anaconda-widgets + - audit + - bind-utils + - bzip2 + - cryptsetup + - curl + - dbus-x11 + - dejavu-sans-fonts + - dejavu-sans-mono-fonts + - device-mapper-persistent-data + - dmidecode + - dnf + - dracut-config-generic + - dracut-network + - efibootmgr + - ethtool + - fcoe-utils + - ftp + - gdb-gdbserver + - glibc-all-langpacks + - gnome-kiosk + - google-noto-sans-cjk-ttc-fonts + - grub2-tools + - grub2-tools-extra + - grub2-tools-minimal + - grubby + - gsettings-desktop-schemas + - hdparm + - hexedit + - hostname + - initscripts + - ipmitool + - jomolhari-fonts + - kbd + - kbd-misc + - kdump-anaconda-addon + - kernel + - less + - libblockdev-lvm-dbus + - libibverbs + - librsvg2 + - linux-firmware + - lldpad + - lsof + - madan-fonts + - mt-st + - mtr + - net-tools + - nfs-utils + - nm-connection-editor + - nmap-ncat + - nss-tools + - openssh-clients + - openssh-server + - ostree + - pciutils + - perl-interpreter + - pigz + - plymouth + - prefixdevname + - python3-pyatspi + - rdma-core + - rng-tools + - rpcbind + - rpm-ostree + - rsync + - rsyslog + - selinux-policy-targeted + - sg3_utils + - sil-padauk-fonts + - smartmontools + - spice-vdagent + - strace + - systemd + - tar + - udisks2 + - udisks2-iscsi + - usbutils + - vim-minimal + - volume_key + - wget + - xfsdump + - xfsprogs + - xrdb + - xz + "centos-10": + when: + # XXX: add almalinux,bazzite,heliumos,auora-helium + distro_name: "bootc-centos" + version_greater_or_equal: "10.0" + append: *pkgset_rhel10 + "rhel-9": + when: + distro_name: "bootc-rhel" + version_greater_or_equal: "9.0" + version_less_than: "10.0" + append: &pkgset_rhel9 + include: + # This is the same set as the Fedora one, but without + # packages not available in CentOS/RHEL: + # atheros-firmware, brcmfmac-firmware, + # iwlwifi-dvm-firmware, iwlwifi-mvm-firmware, + # realtek-firmware, rit-meera-new-fonts + - aajohan-comfortaa-fonts + - abattis-cantarell-fonts + - alsa-firmware + - alsa-tools-firmware + - anaconda + - anaconda-dracut + - anaconda-install-env-deps + - anaconda-widgets + - audit + - bind-utils + - bitmap-fangsongti-fonts + - bzip2 + - cryptsetup + - curl + - dbus-x11 + - dejavu-sans-fonts + - dejavu-sans-mono-fonts + - device-mapper-persistent-data + - dmidecode + - dnf + - dracut-config-generic + - dracut-network + - efibootmgr + - ethtool + - fcoe-utils + - ftp + - gdb-gdbserver + - gdisk + - glibc-all-langpacks + - gnome-kiosk + - google-noto-sans-cjk-ttc-fonts + - grub2-tools + - grub2-tools-extra + - grub2-tools-minimal + - grubby + - gsettings-desktop-schemas + - hdparm + - hexedit + - hostname + - initscripts + - ipmitool + - jomolhari-fonts + - kbd + - kbd-misc + - kdump-anaconda-addon + - kernel + - khmeros-base-fonts + - less + - libblockdev-lvm-dbus + - libibverbs + - libreport-plugin-bugzilla + - libreport-plugin-reportuploader + - librsvg2 + - linux-firmware + - lldpad + - lsof + - madan-fonts + - mt-st + - mtr + - net-tools + - nfs-utils + - nm-connection-editor + - nmap-ncat + - nss-tools + - openssh-clients + - openssh-server + - ostree + - pciutils + - perl-interpreter + - pigz + - plymouth + - prefixdevname + - python3-pyatspi + - rdma-core + - rng-tools + - rpcbind + - rpm-ostree + - rsync + - rsyslog + - selinux-policy-targeted + - sg3_utils + - sil-abyssinica-fonts + - sil-padauk-fonts + - smartmontools + - spice-vdagent + - strace + - systemd + - tar + - tigervnc-server-minimal + - tigervnc-server-module + - udisks2 + - udisks2-iscsi + - usbutils + - vim-minimal + - volume_key + - wget + - xfsdump + - xfsprogs + - xorg-x11-drivers + - xorg-x11-fonts-misc + - xorg-x11-server-Xorg + - xorg-x11-xauth + - xrdb + - xz + "centos-9": + when: + distro_name: "bootc-centos" + version_greater_or_equal: "9.0" + version_less_than: "10.0" + append: *pkgset_rhel10 + "fedora-40plus": + when: + # XXX: add bazzite,aurora,bluefin + distro_name: "bootc-fedora" + version_greater_or_equal: "40" + append: + include: + - aajohan-comfortaa-fonts + - abattis-cantarell-fonts + - alsa-firmware + - alsa-tools-firmware + - anaconda + - anaconda-dracut + - anaconda-install-img-deps + - anaconda-widgets + - atheros-firmware + - audit + - bind-utils + - bitmap-fangsongti-fonts + - brcmfmac-firmware + - bzip2 + - cryptsetup + - curl + - dbus-x11 + - dejavu-sans-fonts + - dejavu-sans-mono-fonts + - device-mapper-persistent-data + - dmidecode + - dnf + - dracut-config-generic + - dracut-network + - efibootmgr + - ethtool + - fcoe-utils + - ftp + - gdb-gdbserver + - gdisk + - glibc-all-langpacks + - gnome-kiosk + - google-noto-sans-cjk-ttc-fonts + - grub2-tools + - grub2-tools-extra + - grub2-tools-minimal + - grubby + - gsettings-desktop-schemas + - hdparm + - hexedit + - hostname + - initscripts + - ipmitool + - iwlwifi-dvm-firmware + - iwlwifi-mvm-firmware + - jomolhari-fonts + - kbd + - kbd-misc + - kdump-anaconda-addon + - kernel + - khmeros-base-fonts + - less + - libblockdev-lvm-dbus + - libibverbs + - libreport-plugin-bugzilla + - libreport-plugin-reportuploader + - librsvg2 + - linux-firmware + - lldpad + - lsof + - madan-fonts + - mt-st + - mtr + - net-tools + - nfs-utils + - nm-connection-editor + - nmap-ncat + - nss-tools + - openssh-clients + - openssh-server + - ostree + - pciutils + - perl-interpreter + - pigz + - plymouth + - prefixdevname + - python3-pyatspi + - rdma-core + - realtek-firmware + - rit-meera-new-fonts + - rng-tools + - rpcbind + - rpm-ostree + - rsync + - rsyslog + - selinux-policy-targeted + - sg3_utils + - sil-abyssinica-fonts + - sil-padauk-fonts + - smartmontools + - spice-vdagent + - strace + - systemd + - tar + - tigervnc-server-minimal + - tigervnc-server-module + - udisks2 + - udisks2-iscsi + - usbutils + - vim-minimal + - volume_key + - wget + - xfsdump + - xfsprogs + - xorg-x11-drivers + - xorg-x11-fonts-misc + - xorg-x11-server-Xorg + - xorg-x11-xauth + - xrdb + - xz diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 9d75518404..108d9e3559 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -335,3 +335,16 @@ distros: x86_64: "registry.access.redhat.com/ubi{{.MajorVersion}}/ub i:latest" ppc64le: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" s390x: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" + + # XXX: ideally we had a "testdata/defs.d" overlay/droping for this + # Note that this will not be visible by default because we do not + # ship with a reporegistry that contains repositories for it + - name: bootc-centos-9 + default_fs_type: "xfs" + vendor: centos + defs_path: bootc + # the installer needs those + distro_like: rhel-9 + release_version: 9 + os_version: 9 + iso_label_tmpl: "CentOS-Stream-{{.Distro.MajorVersion}}-BaseOS-{{.Arch}}" diff --git a/pkg/distro/defs/loader.go b/pkg/distro/defs/loader.go index 5587ef37dd..2746b3997f 100644 --- a/pkg/distro/defs/loader.go +++ b/pkg/distro/defs/loader.go @@ -223,39 +223,50 @@ func NewDistroYAML(nameVer string) (*DistroYAML, error) { return nil, err } - // load imageTypes - f, err := dataFS().Open(filepath.Join(foundDistro.DefsPath, "distro.yaml")) - if err != nil { + if err := foundDistro.LoadImageTypes(); err != nil { return nil, err } + + return foundDistro, nil +} + +// LoadImageTypes loads the images types for the current distribution +func (d *DistroYAML) LoadImageTypes() error { + f, err := dataFS().Open(filepath.Join(d.DefsPath, "distro.yaml")) + if err != nil { + return err + } defer f.Close() - var toplevel imageTypesYAML decoder := yaml.NewDecoder(f) decoder.KnownFields(true) + + var toplevel imageTypesYAML if err := decoder.Decode(&toplevel); err != nil { - return nil, err + return err } if len(toplevel.ImageTypes) > 0 { - foundDistro.imageTypes = make(map[string]ImageTypeYAML, len(toplevel.ImageTypes)) + d.imageTypes = make(map[string]ImageTypeYAML, len(toplevel.ImageTypes)) for name := range toplevel.ImageTypes { v := toplevel.ImageTypes[name] - v.name = name - if err := v.runTemplates(foundDistro); err != nil { - return nil, err + if err := v.runTemplates(d); err != nil { + return err } - foundDistro.imageTypes[name] = v + if err := v.setupDefaultFS(d.DefaultFSType.String()); err != nil { + return err + } + v.name = name + d.imageTypes[name] = v } } - foundDistro.imageConfig, err = toplevel.ImageConfig.For(nameVer) + d.imageConfig, err = toplevel.ImageConfig.For(d.Name) if err != nil { - return nil, err + return err } - return foundDistro, nil + return nil } -// imageTypesYAML describes the image types for a given distribution // family. Note that multiple distros may use the same image types, // e.g. centos/rhel type imageTypesYAML struct { @@ -477,6 +488,44 @@ func (it *ImageTypeYAML) runTemplates(distro *DistroYAML) error { return nil } +func (it *ImageTypeYAML) setupDefaultFS(distroDefaultFS string) error { + subs := func(pts map[string]*disk.PartitionTable) error { + for _, pt := range pts { + err := pt.ForEachMountable(func(mnt disk.Mountable, _ []disk.Entity) error { + elem, ok := mnt.(*disk.Filesystem) + if !ok { + return nil + } + if elem.Type == "distro-default" { + if distroDefaultFS == "" { + return fmt.Errorf("mount %q requires a default filesystem for the distribution but none set", mnt.GetMountpoint()) + } + elem.Type = distroDefaultFS + } + return nil + }) + if err != nil { + return err + } + } + return nil + } + // we need to update both the partition tables and all + // partition tables overrides + if err := subs(it.PartitionTables); err != nil { + return err + } + if it.PartitionTablesOverrides != nil { + for _, cond := range it.PartitionTablesOverrides.Conditions { + if err := subs(cond.Override); err != nil { + return err + } + } + } + + return nil +} + type platformsOverride struct { Conditions map[string]*conditionsPlatforms `yaml:"conditions,omitempty"` } diff --git a/pkg/distro/defs/loader_test.go b/pkg/distro/defs/loader_test.go index d893e281d8..8e6b87d86a 100644 --- a/pkg/distro/defs/loader_test.go +++ b/pkg/distro/defs/loader_test.go @@ -317,6 +317,127 @@ image_types: }, partTable) } +func TestDefsPartitionTableFilesystemDistroDefault(t *testing.T) { + fakeDistrosYaml := ` +distros: + - name: test-distro-1 + defs_path: test-distro + default_fs_type: ext4 +` + fakeImageTypesYaml := ` +image_types: + test_type: + filename: test.img + platforms: + - arch: x86_64 + partition_table: + test_arch: + partitions: + - payload_type: filesystem + payload: + type: distro-default + mountpoint: "/" +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYaml, fakeImageTypesYaml) + restore := defs.MockDataFS(baseDir) + defer restore() + distro, err := defs.NewDistroYAML("test-distro-1") + require.NoError(t, err) + it := distro.ImageTypes()["test_type"] + require.NotNil(t, it) + + partTable, err := it.PartitionTable("test-distro-1", "test_arch") + require.NoError(t, err) + assert.Equal(t, &disk.PartitionTable{ + Partitions: []disk.Partition{ + { + Payload: &disk.Filesystem{ + Type: "ext4", + Mountpoint: "/", + }, + }, + }, + }, partTable) +} + +func TestDefsPartitionTableFilesystemPartTableOverride(t *testing.T) { + fakeDistrosYaml := ` +distros: + - name: test-distro-1 + defs_path: test-distro + default_fs_type: ext4 +` + fakeImageTypesYaml := ` +image_types: + test_type: + filename: test.img + platforms: + - arch: x86_64 + partition_table: + x86_64: + partitions: + partition_tables_override: + conditions: + "test condition": + when: + distro_name: test-distro + override: + x86_64: + partitions: + - payload_type: filesystem + payload: + type: distro-default + mountpoint: "/" +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYaml, fakeImageTypesYaml) + restore := defs.MockDataFS(baseDir) + defer restore() + distro, err := defs.NewDistroYAML("test-distro-1") + require.NoError(t, err) + it := distro.ImageTypes()["test_type"] + require.NotNil(t, it) + + partTable, err := it.PartitionTable("test-distro-1", "x86_64") + require.NoError(t, err) + assert.Equal(t, &disk.PartitionTable{ + Partitions: []disk.Partition{ + { + Payload: &disk.Filesystem{ + Type: "ext4", + Mountpoint: "/", + }, + }, + }, + }, partTable) +} + +func TestDefsPartitionTableFilesystemDistroDefaultErr(t *testing.T) { + fakeDistrosYaml := ` +distros: + - name: test-distro-1 + defs_path: test-distro +` + fakeImageTypesYaml := ` +image_types: + test_type: + filename: test.img + platforms: + - arch: x86_64 + partition_table: + test_arch: + partitions: + - payload_type: filesystem + payload: + type: distro-default + mountpoint: "/" +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYaml, fakeImageTypesYaml) + restore := defs.MockDataFS(baseDir) + defer restore() + _, err := defs.NewDistroYAML("test-distro-1") + assert.EqualError(t, err, `mount "/" requires a default filesystem for the distribution but none set`) +} + var fakeImageTypesYaml = ` image_types: test_type: diff --git a/pkg/distro/distro.go b/pkg/distro/distro.go index e5bedeec9c..8dfab157e8 100644 --- a/pkg/distro/distro.go +++ b/pkg/distro/distro.go @@ -131,10 +131,18 @@ type ImageType interface { Manifest(bp *blueprint.Blueprint, options ImageOptions, repos []rpmmd.RepoConfig, seed *int64) (*manifest.Manifest, []string, error) } +type BootcRef struct { + Imgref *string `json:"imgref,omitempty"` + BuildImgref *string `json:"build_imgref,omitempty"` +} + // The ImageOptions specify options for a specific image build type ImageOptions struct { - Size uint64 `json:"size"` - OSTree *ostree.ImageOptions `json:"ostree,omitempty"` + Size uint64 `json:"size"` + OSTree *ostree.ImageOptions `json:"ostree,omitempty"` + + Bootc *BootcRef `json:"bootc,omitempty"` + Subscription *subscription.ImageOptions `json:"subscription,omitempty"` Facts *facts.ImageOptions `json:"facts,omitempty"` PartitioningMode disk.PartitioningMode `json:"partitioning-mode,omitempty"` diff --git a/pkg/distro/generic/distro.go b/pkg/distro/generic/distro.go index d5cba182de..34de4b5d0c 100644 --- a/pkg/distro/generic/distro.go +++ b/pkg/distro/generic/distro.go @@ -8,8 +8,12 @@ import ( "text/template" "github.com/osbuild/images/internal/common" + "github.com/osbuild/images/pkg/bib/container" + "github.com/osbuild/images/pkg/bib/osinfo" + "github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/distro/defs" + "github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/platform" ) @@ -68,6 +72,67 @@ func (d *distribution) getISOLabelFunc(isoLabel string) isoLabelFunc { } } +// XXX: wrong layer +func ImageFromBootc(bootcRef, imgTypeStr, archStr, defaultFs string) (distro.ImageType, error) { + cnt, err := container.New(bootcRef) + if err != nil { + return nil, err + } + defer cnt.Stop() + + info, err := osinfo.Load(cnt.Root()) + if err != nil { + return nil, err + } + if defaultFs == "" { + defaultFs, err = cnt.DefaultRootfsType() + if err != nil { + return nil, err + } + } + rootfsType, err := disk.NewFSType(defaultFs) + if err != nil { + return nil, err + } + // best effort to find a uefiVedor + uefiVendor := info.UEFIVendor + if uefiVendor == "" { + uefiVendor = info.OSRelease.ID + } + + nameVer := fmt.Sprintf("bootc-%s-%s", info.OSRelease.ID, info.OSRelease.VersionID) + rd := &distribution{ + DistroYAML: defs.DistroYAML{ + Name: nameVer, + DefaultFSType: rootfsType, + DefsPath: "./bootc", + Vendor: uefiVendor, + // DistroLike is used to check valid options + // on manifest generation mostly so have a + // custom for now, not orthogonal but best we + // can do for now + DistroLike: manifest.DISTRO_BOOTC, + }, + arches: make(map[string]*architecture), + } + if err := rd.DistroYAML.LoadImageTypes(); err != nil { + return nil, err + } + if err := rd.populateArchitectures(); err != nil { + return nil, err + } + a, err := rd.GetArch(archStr) + if err != nil { + return nil, err + } + imgType, err := a.GetImageType(imgTypeStr) + if err != nil { + return nil, err + } + + return imgType, nil +} + func newDistro(nameVer string) (distro.Distro, error) { distroYAML, err := defs.NewDistroYAML(nameVer) if err != nil { @@ -81,16 +146,22 @@ func newDistro(nameVer string) (distro.Distro, error) { DistroYAML: *distroYAML, arches: make(map[string]*architecture), } + if err := rd.populateArchitectures(); err != nil { + return nil, err + } + return rd, nil +} - for _, imgTypeYAML := range distroYAML.ImageTypes() { +func (rd *distribution) populateArchitectures() error { + for _, imgTypeYAML := range rd.DistroYAML.ImageTypes() { // use as marker for images that are not converted to // YAML yet if imgTypeYAML.Filename == "" { continue } - platforms, err := imgTypeYAML.PlatformsFor(nameVer) + platforms, err := imgTypeYAML.PlatformsFor(rd.Name()) if err != nil { - return nil, err + return err } for _, pl := range platforms { ar, ok := rd.arches[pl.Arch.String()] @@ -98,17 +169,17 @@ func newDistro(nameVer string) (distro.Distro, error) { ar = newArchitecture(rd, pl.Arch.String()) rd.arches[pl.Arch.String()] = ar } - if distroYAML.SkipImageType(imgTypeYAML.Name(), pl.Arch.String()) { + if rd.DistroYAML.SkipImageType(imgTypeYAML.Name(), pl.Arch.String()) { continue } it := newImageTypeFrom(rd, ar, imgTypeYAML) if err := ar.addImageType(&pl, it); err != nil { - return nil, err + return err } } } - return rd, nil + return nil } func (d *distribution) Name() string { diff --git a/pkg/distro/generic/images.go b/pkg/distro/generic/images.go index 4ff575a458..724827b19d 100644 --- a/pkg/distro/generic/images.go +++ b/pkg/distro/generic/images.go @@ -721,6 +721,49 @@ func bootableContainerImage(workload workload.Workload, return img, nil } +func bootcDiskImage(workload workload.Workload, + t *imageType, + bp *blueprint.Blueprint, + options distro.ImageOptions, + packageSets map[string]rpmmd.PackageSet, + containers []container.SourceSpec, + rng *rand.Rand) (image.ImageKind, error) { + + if options.Bootc == nil || options.Bootc.Imgref == nil { + return nil, fmt.Errorf("no bootc base image defined") + } + containerSource := container.SourceSpec{ + Source: *options.Bootc.Imgref, + Name: *options.Bootc.Imgref, + Local: true, + } + buildContainerSource := containerSource + if options.Bootc.BuildImgref != nil { + buildContainerSource = container.SourceSpec{ + Source: *options.Bootc.BuildImgref, + Name: *options.Bootc.BuildImgref, + Local: true, + } + } + img := image.NewBootcDiskImage(containerSource, buildContainerSource) + img.Platform = t.platform + + var err error + img.OSCustomizations, err = osCustomizations(t, packageSets[osPkgsKey], options, containers, bp.Customizations) + if err != nil { + return nil, err + } + + pt, err := t.getPartitionTable(bp.Customizations, options, rng) + if err != nil { + return nil, err + } + img.PartitionTable = pt + img.Filename = t.Filename() + + return img, nil +} + func iotContainerImage(workload workload.Workload, t *imageType, bp *blueprint.Blueprint, @@ -850,6 +893,82 @@ func iotInstallerImage(workload workload.Workload, return img, nil } +func bootcAnacondaInstallerImage(workload workload.Workload, + t *imageType, + bp *blueprint.Blueprint, + options distro.ImageOptions, + packageSets map[string]rpmmd.PackageSet, + containers []container.SourceSpec, + rng *rand.Rand) (image.ImageKind, error) { + + if options.Bootc == nil || options.Bootc.Imgref == nil { + return nil, fmt.Errorf("no bootc base image defined") + } + containerSource := container.SourceSpec{ + Source: *options.Bootc.Imgref, + Name: *options.Bootc.Imgref, + Local: true, + } + + d := t.arch.distro + + // The ref is not needed and will be removed from the ctor later + // in time + img := image.NewAnacondaContainerInstaller(containerSource, "") + img.ContainerRemoveSignatures = true + img.RootfsCompression = "zstd" + + img.Platform = t.platform + img.ExtraBasePackages = packageSets[installerPkgsKey] + + // XXX: double check this was OSRelease.{Name,VersionID} + img.Product = d.Name() + img.OSVersion = d.OsVersion() + + var err error + img.ISOLabel, err = t.ISOLabel() + if err != nil { + return nil, err + } + + customizations := bp.Customizations + img.FIPS = customizations.GetFIPS() + img.Kickstart, err = kickstart.New(customizations) + if err != nil { + return 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, err + } + if instCust != nil && instCust.Modules != nil { + img.AdditionalAnacondaModules = append(img.AdditionalAnacondaModules, instCust.Modules.Enable...) + img.DisabledAnacondaModules = append(img.DisabledAnacondaModules, instCust.Modules.Disable...) + } + img.AdditionalAnacondaModules = append(img.AdditionalAnacondaModules, + anaconda.ModuleUsers, + anaconda.ModuleServices, + anaconda.ModuleSecurity, + ) + + img.Kickstart.OSTree = &kickstart.OSTree{ + OSName: "default", + } + // XXX: TODO this sucks + img.UseRHELLoraxTemplates = (d.DistroYAML.DistroLike == manifest.DISTRO_EL10 || d.DistroYAML.DistroLike == manifest.DISTRO_EL9 || d.DistroYAML.DistroLike == manifest.DISTRO_EL8 || d.DistroYAML.DistroLike == manifest.DISTRO_EL7) + // see https://github.com/osbuild/bootc-image-builder/issues/733 + img.RootfsType = manifest.SquashfsRootfs + img.Filename = "install.iso" + + return img, nil +} + func iotImage(workload workload.Workload, t *imageType, bp *blueprint.Blueprint, diff --git a/pkg/distro/generic/imagetype.go b/pkg/distro/generic/imagetype.go index 5197c42988..77e67e8a43 100644 --- a/pkg/distro/generic/imagetype.go +++ b/pkg/distro/generic/imagetype.go @@ -20,6 +20,7 @@ import ( "github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/platform" "github.com/osbuild/images/pkg/rpmmd" + "github.com/osbuild/images/pkg/runner" ) type imageFunc func(workload workload.Workload, t *imageType, bp *blueprint.Blueprint, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error) @@ -48,6 +49,10 @@ func newImageTypeFrom(d *distribution, ar *architecture, imgYAML defs.ImageTypeY switch imgYAML.Image { case "disk": it.image = diskImage + case "bootc_disk": + it.image = bootcDiskImage + case "bootc_installer": + it.image = bootcAnacondaInstallerImage case "container": it.image = containerImage case "image_installer": @@ -162,6 +167,7 @@ func (t *imageType) getPartitionTable(customizations *blueprint.Customizations, return nil, err } + defaultFSType := t.arch.distro.DefaultFSType imageSize := t.Size(options.Size) partitioning, err := customizations.GetPartitioning() if err != nil { @@ -179,7 +185,7 @@ func (t *imageType) getPartitionTable(customizations *blueprint.Customizations, partOptions := &disk.CustomPartitionTableOptions{ PartitionTableType: basePartitionTable.Type, // PT type is not customizable, it is determined by the base PT for an image type or architecture BootMode: t.BootMode(), - DefaultFSType: t.arch.distro.DefaultFSType, + DefaultFSType: defaultFSType, RequiredMinSizes: t.ImageTypeYAML.RequiredPartitionSizes, Architecture: t.platform.GetArch(), } @@ -187,7 +193,7 @@ func (t *imageType) getPartitionTable(customizations *blueprint.Customizations, } mountpoints := customizations.GetFilesystems() - return disk.NewPartitionTable(basePartitionTable, mountpoints, imageSize, options.PartitioningMode, t.platform.GetArch(), t.ImageTypeYAML.RequiredPartitionSizes, rng) + return disk.NewPartitionTable(basePartitionTable, mountpoints, imageSize, options.PartitioningMode, t.platform.GetArch(), t.ImageTypeYAML.RequiredPartitionSizes, defaultFSType.String(), rng) } func (t *imageType) getDefaultImageConfig() *distro.ImageConfig { @@ -303,20 +309,40 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint, if err != nil { return nil, nil, err } + mf := manifest.New() - // TODO: remove the need for this entirely, the manifest has a - // bunch of code that checks the distro currently, ideally all - // would just be encoded in the YAML - mf.Distro = t.arch.distro.DistroYAML.DistroLike - if mf.Distro == manifest.DISTRO_NULL { - return nil, nil, fmt.Errorf("no distro_like set in yaml for %q", t.arch.distro.Name()) - } - if options.UseBootstrapContainer { - mf.DistroBootstrapRef = bootstrapContainerFor(t) - } - _, err = img.InstantiateManifest(&mf, repos, &t.arch.distro.DistroYAML.Runner, rng) - if err != nil { - return nil, nil, err + // TODO: consider changing the ImageKind.InstantiateManifest interface + // to take a "container.SourceSpec" or a generalized struct with both + // distros/containers + switch img := img.(type) { + case image.BootcImageKind: + containerSource := container.SourceSpec{ + Source: *options.Bootc.Imgref, + Name: *options.Bootc.Imgref, + Local: true, + } + mf.Distro = manifest.DISTRO_NULL + runner := &runner.Linux{} + // XXX: this is incorrect, this can also be an + // installer and then its a "normal" iso and we don't + // run manifestFromContainer + if err := img.InstantiateManifestFromContainers(&mf, []container.SourceSpec{containerSource}, runner, rng); err != nil { + return nil, nil, err + } + default: + // TODO: remove the need for this entirely, the manifest has a + // bunch of code that checks the distro currently, ideally all + // would just be encoded in the YAML + mf.Distro = t.arch.distro.DistroYAML.DistroLike + if mf.Distro == manifest.DISTRO_NULL { + return nil, nil, fmt.Errorf("no distro_like set in yaml for %q", t.arch.distro.Name()) + } + if options.UseBootstrapContainer { + mf.DistroBootstrapRef = bootstrapContainerFor(t) + } + if _, err = img.InstantiateManifest(&mf, repos, &t.arch.distro.DistroYAML.Runner, rng); err != nil { + return nil, nil, err + } } return &mf, warnings, err @@ -341,6 +367,8 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp return checkOptionsRhel9(t, bp, options) case manifest.DISTRO_EL10: return checkOptionsRhel10(t, bp, options) + case manifest.DISTRO_BOOTC: + return checkOptionsBootc(t, bp, options) default: return nil, fmt.Errorf("checkOptions called with unknown distro-like %v", idLike) } diff --git a/pkg/distro/generic/options.go b/pkg/distro/generic/options.go index 2b9c0c99ac..abfbf4772a 100644 --- a/pkg/distro/generic/options.go +++ b/pkg/distro/generic/options.go @@ -1,6 +1,7 @@ package generic import ( + "errors" "fmt" "slices" "strings" @@ -9,7 +10,9 @@ import ( "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/blueprint" "github.com/osbuild/images/pkg/customizations/oscap" + "github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/pathpolicy" "github.com/osbuild/images/pkg/policies" ) @@ -711,3 +714,57 @@ func checkOptionsFedora(t *imageType, bp *blueprint.Blueprint, options distro.Im return warnings, nil } + +// checkBootcMountpoints is needed because /var is not allowed, but we +// need to allow any subdirectories that denied by BootcMountPointPolicy +func checkBootcMountpoints(filesystems []blueprint.FilesystemCustomization, policy *pathpolicy.PathPolicies) error { + errs := []error{} + for _, fs := range filesystems { + if err := policy.Check(fs.Mountpoint); err != nil { + errs = append(errs, err) + } + if fs.Mountpoint == "/var" { + // this error message is consistent with the errors returned by policy.Check() + // TODO: remove trailing space inside the quoted path when the function is fixed in osbuild/images. + errs = append(errs, fmt.Errorf(`path "/var" is not allowed`)) + } + } + if len(errs) > 0 { + return fmt.Errorf("the following errors occurred while validating custom mountpoints:\n%w", errors.Join(errs...)) + } + return nil +} + +func checkOptionsBootc(t *imageType, bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) { + var warnings []string + + customizations := bp.Customizations + dc := customizations.GetDirectories() + fc := customizations.GetFiles() + if err := blueprint.ValidateDirFileCustomizations(dc, fc); err != nil { + return nil, err + } + if err := blueprint.CheckDirectoryCustomizationsPolicy(dc, policies.BootcCustomDirectoriesPolicies); err != nil { + return nil, err + } + if err := blueprint.CheckFileCustomizationsPolicy(fc, policies.BootcCustomFilesPolicies); err != nil { + return nil, err + } + + var policy *pathpolicy.PathPolicies + switch options.PartitioningMode { + case disk.BtrfsPartitioningMode: + // btrfs subvolumes are not supported at build time yet, so we only + // allow / and /boot to be customized when building a btrfs disk (the + // minimal policy) + policy = policies.BootcMountpointMinimalPolicy + default: + policy = policies.BootcMountpointPolicies + } + fsCust := customizations.GetFilesystems() + if err := checkBootcMountpoints(fsCust, policy); err != nil { + return nil, err + } + + return warnings, nil +} diff --git a/pkg/image/bootc_disk.go b/pkg/image/bootc_disk.go index 1225360aba..fb2958a457 100644 --- a/pkg/image/bootc_disk.go +++ b/pkg/image/bootc_disk.go @@ -3,13 +3,16 @@ package image import ( "fmt" "math/rand" + "strings" + "github.com/osbuild/images/pkg/artifact" "github.com/osbuild/images/pkg/container" "github.com/osbuild/images/pkg/customizations/fsnode" "github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/platform" + "github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/runner" ) @@ -36,6 +39,18 @@ func NewBootcDiskImage(container container.SourceSpec, buildContainer container. } } +// XXX: consider what to do here, ideally the InstanciateManifest +// signature would change to pass containers (or a new union of +// repos and containers) so that we can use the same method +// everywhere +func (img *BootcDiskImage) InstantiateManifest(m *manifest.Manifest, + repos []rpmmd.RepoConfig, + runner runner.Runner, + rng *rand.Rand) (*artifact.Artifact, error) { + + return nil, fmt.Errorf("internal error: BootcDiskImage only supported InstantiateManifestFromContainers") +} + func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifest, containers []container.SourceSpec, runner runner.Runner, @@ -112,7 +127,9 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes // In BIB, we export multiple images from the same pipeline so we use the // filename as the basename for each export and set the extensions based on // each file format. - fileBasename := img.Filename + // + // XXX: how to converge these two worlds? + fileBasename := strings.SplitN(img.Filename, ".", 2)[0] rawImage.SetFilename(fmt.Sprintf("%s.raw", fileBasename)) qcow2Pipeline := manifest.NewQCOW2(hostPipeline, rawImage) diff --git a/pkg/image/image.go b/pkg/image/image.go index 88c6752ec0..4adcf5ecb1 100644 --- a/pkg/image/image.go +++ b/pkg/image/image.go @@ -5,6 +5,7 @@ import ( "math/rand" "github.com/osbuild/images/pkg/artifact" + "github.com/osbuild/images/pkg/container" "github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/runner" @@ -15,6 +16,11 @@ type ImageKind interface { InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner runner.Runner, rng *rand.Rand) (*artifact.Artifact, error) } +type BootcImageKind interface { + Name() string + InstantiateManifestFromContainers(m *manifest.Manifest, containers []container.SourceSpec, runner runner.Runner, rng *rand.Rand) error +} + type Base struct { name string } diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index 923b0b1752..edcd764cb5 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -33,6 +33,7 @@ const ( DISTRO_EL8 DISTRO_EL7 DISTRO_FEDORA + DISTRO_BOOTC ) func (d *Distro) UnmarshalJSON(data []byte) error { @@ -52,6 +53,8 @@ func (d *Distro) UnmarshalJSON(data []byte) error { *d = DISTRO_EL7 case "fedora": *d = DISTRO_FEDORA + case "bootc": + *d = DISTRO_BOOTC default: return fmt.Errorf("unknown distro: %q", s) } diff --git a/pkg/manifestgen/manifestgen.go b/pkg/manifestgen/manifestgen.go index 927070f569..2a3f45f9a7 100644 --- a/pkg/manifestgen/manifestgen.go +++ b/pkg/manifestgen/manifestgen.go @@ -10,11 +10,14 @@ import ( "slices" "strings" + "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/blueprint" "github.com/osbuild/images/pkg/container" "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/dnfjson" + "github.com/osbuild/images/pkg/experimentalflags" "github.com/osbuild/images/pkg/manifest" + "github.com/osbuild/images/pkg/manifestgen/manifestmock" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/ostree" "github.com/osbuild/images/pkg/reporegistry" @@ -129,6 +132,12 @@ func New(reporegistry *reporegistry.RepoRegistry, opts *Options) (*Generator, er if mg.commitResolver == nil { mg.commitResolver = DefaultCommitResolver } + if experimentalflags.Bool("bib-mock-resolvers") { + mg.containerResolver = func(containerSources map[string][]container.SourceSpec, archName string) (map[string][]container.Spec, error) { + // XXX: we don't consider arch right now + return manifestmock.ResolveContainers(containerSources), nil + } + } return mg, nil } @@ -174,6 +183,16 @@ func (mg *Generator) Generate(bp *blueprint.Blueprint, dist distro.Distro, imgTy if err != nil { return err } + for _, specs := range containerSpecs { + for _, spec := range specs { + // XXX: if only distro.Arch had .Arch() + if spec.Arch != arch.ARCH_UNSET && spec.Arch.String() != a.Name() { + // XXX: add ErrContainerResolveArchmistmatch type + return fmt.Errorf("image found is for unexpected architecture %q (expected %q)", spec.Arch, a) + } + } + } + commitSpecs, err := mg.commitResolver(preManifest.GetOSTreeSourceSpecs()) if err != nil { return err diff --git a/pkg/osbuild/device_test.go b/pkg/osbuild/device_test.go index bb3dc1aaec..306951860b 100644 --- a/pkg/osbuild/device_test.go +++ b/pkg/osbuild/device_test.go @@ -23,7 +23,7 @@ func TestGenDeviceCreationStages(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, make(map[string]uint64), rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_AARCH64, make(map[string]uint64), "", rng) assert.NoError(err) stages := GenDeviceCreationStages(pt, "image.raw") @@ -86,7 +86,7 @@ func TestGenDeviceFinishStages(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_PPC64LE, make(map[string]uint64), rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_PPC64LE, make(map[string]uint64), "", rng) assert.NoError(err) stages := GenDeviceFinishStages(pt, "image.raw") @@ -129,7 +129,7 @@ func TestGenDeviceFinishStagesOrderWithLVMClevisBind(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm+clevisBind"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_S390X, make(map[string]uint64), rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_S390X, make(map[string]uint64), "", rng) assert.NoError(err) stages := GenDeviceFinishStages(pt, "image.raw") diff --git a/pkg/osbuild/disk_test.go b/pkg/osbuild/disk_test.go index 2a93ee54a6..4b544603ac 100644 --- a/pkg/osbuild/disk_test.go +++ b/pkg/osbuild/disk_test.go @@ -43,7 +43,7 @@ func TestGenImageKernelOptions(t *testing.T) { luks_lvm := testPartitionTables["luks+lvm"] - pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, make(map[string]uint64), rng) + pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, disk.AutoLVMPartitioningMode, arch.ARCH_X86_64, make(map[string]uint64), "", rng) assert.NoError(err) uuids := collectUUIDs(pt) diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index e55b3a9396..cafe19d35c 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -117,3 +117,50 @@ var OstreeCustomFilesPolicies = pathpolicy.NewPathPolicies(map[string]pathpolicy "/etc/passwd": {Deny: true}, "/etc/group": {Deny: true}, }) + +// BootcMountpointPolciies are more restrictive than the ostree +// mountpoint policy defined in osbuild/images. It only allows / (for +// sizing the root partition) and custom mountpoints under /var but +// not /var itself. + +// Since our policy library doesn't support denying a path while allowing +// its subpaths (only the opposite), we augment the standard policy check +// with a simple search through the custom mountpoints to deny /var +// specifically. +var BootcMountpointPolicies = pathpolicy.NewPathPolicies(map[string]pathpolicy.PathPolicy{ + // allow all existing mountpoints (but no subdirs) to support size customizations + "/": {Deny: false, Exact: true}, + "/boot": {Deny: false, Exact: true}, + + // /var is not allowed, but we need to allow any subdirectories that + // are not denied below, so we allow it initially and then check it + // separately (in checkMountpoints()) + "/var": {Deny: false}, + + // /var subdir denials + "/var/home": {Deny: true}, + "/var/lock": {Deny: true}, // symlink to ../run/lock which is on tmpfs + "/var/mail": {Deny: true}, // symlink to spool/mail + "/var/mnt": {Deny: true}, + "/var/roothome": {Deny: true}, + "/var/run": {Deny: true}, // symlink to ../run which is on tmpfs + "/var/srv": {Deny: true}, + "/var/usrlocal": {Deny: true}, +}) + +// BootcMountpointMinimalPolicy is a minimal policy for btrfs +// +// btrfs subvolumes are not supported at build time yet, so we only +// allow / and /boot to be customized when building a btrfs disk (the +// minimal policy) +var BootcMountpointMinimalPolicy = pathpolicy.NewPathPolicies(map[string]pathpolicy.PathPolicy{ + // allow all existing mountpoints to support size customizations + "/": {Deny: false, Exact: true}, + "/boot": {Deny: false, Exact: true}, +}) + +// BootcCustomDirectoryPolicies follow the same rules as ostree right now +var BootcCustomDirectoriesPolicies = OstreeCustomDirectoriesPolicies + +// BootcCustomFilesPolicies follow the same rules as ostree right now +var BootcCustomFilesPolicies = OstreeCustomFilesPolicies diff --git a/test/config-map.json b/test/config-map.json index 700397e8ee..d3586e322a 100644 --- a/test/config-map.json +++ b/test/config-map.json @@ -70,6 +70,20 @@ "edge-simplified-installer" ] }, + "./configs/bootc-empty.json": { + "distros": [ + "bootc-*" + ], + "image-types": [ + "ami", + "raw", + "qcow2", + "vmdk", + "vhd", + "gce", + "anaconda-iso" + ] + }, "./configs/edge-ostree-pull-empty.json": { "image-types": [ "edge-installer", diff --git a/test/configs/bootc-empty.json b/test/configs/bootc-empty.json new file mode 100644 index 0000000000..402f721779 --- /dev/null +++ b/test/configs/bootc-empty.json @@ -0,0 +1,11 @@ +{ + "name": "bootc-empty", + "options": { + "bootc": { + "imgref": "quay.io/centos-bootc/centos-bootc:stream9" + } + }, + "depends": { + "config": "empty.json" + } +} diff --git a/test/data/repositories/bootc-centos-9.json b/test/data/repositories/bootc-centos-9.json new file mode 100644 index 0000000000..5d3c504ca4 --- /dev/null +++ b/test/data/repositories/bootc-centos-9.json @@ -0,0 +1,106 @@ +{ + "aarch64": [ + { + "name": "AppStream", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-aarch64-appstream-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "BaseOS", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-aarch64-baseos-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + } + ], + "ppc64le": [ + { + "name": "AppStream", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-ppc64le-appstream-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "BaseOS", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-ppc64le-baseos-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + } + ], + "s390x": [ + { + "name": "AppStream", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-s390x-appstream-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "BaseOS", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-s390x-baseos-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + } + ], + "x86_64": [ + { + "name": "AppStream", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-x86_64-appstream-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "BaseOS", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-x86_64-baseos-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "RT", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/cs9-x86_64-rt-20250605", + "check_gpg": true, + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "google-compute-engine", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/el9-x86_64-google-compute-engine-20250605", + "check_gpg": false, + "image_type_tags": [ + "gce" + ], + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQENBGCRt7MBCADkYJHHQQoL6tKrW/LbmfR9ljz7ib2aWno4JO3VKQvLwjyUMPpq\n/SXXMOnx8jXwgWizpPxQYDRJ0SQXS9ULJ1hXRL/OgMnZAYvYDeV2jBnKsAIEdiG/\ne1qm8P4W9qpWJc+hNq7FOT13RzGWRx57SdLWSXo0KeY38r9lvjjOmT/cuOcmjwlD\nT9XYf/RSO+yJ/AsyMdAr+ZbDeQUd9HYJiPdI04lGaGM02MjDMnx+monc+y54t+Z+\nry1WtQdzoQt9dHlIPlV1tR+xV5DHHsejCZxu9TWzzSlL5wfBBeEz7R/OIzivGJpW\nQdJzd+2QDXSRg9q2XYWP5ZVtSgjVVJjNlb6ZABEBAAG0VEFydGlmYWN0IFJlZ2lz\ndHJ5IFJlcG9zaXRvcnkgU2lnbmVyIDxhcnRpZmFjdC1yZWdpc3RyeS1yZXBvc2l0\nb3J5LXNpZ25lckBnb29nbGUuY29tPokBTgQTAQoAOBYhBDW6oLM+nrOW9ZyoOMC6\nXObcYxWjBQJgkbezAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEMC6XObc\nYxWj+igIAMFh6DrAYMeq9sbZ1ZG6oAMrinUheGQbEqe76nIDQNsZnhDwZ2wWqgVC\n7DgOMqlhQmOmzm7M6Nzmq2dvPwq3xC2OeI9fQyzjT72deBTzLP7PJok9PJFOMdLf\nILSsUnmMsheQt4DUO0jYAX2KUuWOIXXJaZ319QyoRNBPYa5qz7qXS7wHLOY89IDq\nfHt6Aud8ER5zhyOyhytcYMeaGC1g1IKWmgewnhEq02FantMJGlmmFi2eA0EPD02G\nC3742QGqRxLwjWsm5/TpyuU24EYKRGCRm7QdVIo3ugFSetKrn0byOxWGBvtu4fH8\nXWvZkRT+u+yzH1s5yFYBqc2JTrrJvRU=\n=zwB2\n-----END PGP PUBLIC KEY BLOCK-----", + "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1\n\nmQENBFWKtqgBCADmKQWYQF9YoPxLEQZ5XA6DFVg9ZHG4HIuehsSJETMPQ+W9K5c5\nUs5assCZBjG/k5i62SmWb09eHtWsbbEgexURBWJ7IxA8kM3kpTo7bx+LqySDsSC3\n/8JRkiyibVV0dDNv/EzRQsGDxmk5Xl8SbQJ/C2ECSUT2ok225f079m2VJsUGHG+5\nRpyHHgoMaRNedYP8ksYBPSD6sA3Xqpsh/0cF4sm8QtmsxkBmCCIjBa0B0LybDtdX\nXIq5kPJsIrC2zvERIPm1ez/9FyGmZKEFnBGeFC45z5U//pHdB1z03dYKGrKdDpID\n17kNbC5wl24k/IeYyTY9IutMXvuNbVSXaVtRABEBAAG0Okdvb2dsZSBDbG91ZCBQ\nYWNrYWdlcyBSUE0gU2lnbmluZyBLZXkgPGdjLXRlYW1AZ29vZ2xlLmNvbT6JATgE\nEwECACIFAlWKtqgCGy8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPCcOUw+\nG6jV+QwH/0wRH+XovIwLGfkg6kYLEvNPvOIYNQWnrT6zZ+XcV47WkJ+i5SR+QpUI\nudMSWVf4nkv+XVHruxydafRIeocaXY0E8EuIHGBSB2KR3HxG6JbgUiWlCVRNt4Qd\n6udC6Ep7maKEIpO40M8UHRuKrp4iLGIhPm3ELGO6uc8rks8qOBMH4ozU+3PB9a0b\nGnPBEsZdOBI1phyftLyyuEvG8PeUYD+uzSx8jp9xbMg66gQRMP9XGzcCkD+b8w1o\n7v3J3juKKpgvx5Lqwvwv2ywqn/Wr5d5OBCHEw8KtU/tfxycz/oo6XUIshgEbS/+P\n6yKDuYhRp6qxrYXjmAszIT25cftb4d4=\n=/PbX\n-----END PGP PUBLIC KEY BLOCK-----" + ] + }, + { + "name": "google-cloud-sdk", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el9/el9-x86_64-google-cloud-sdk-20250605", + "check_gpg": false, + "image_type_tags": [ + "gce" + ], + "gpgkeys": [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQENBGCRt7MBCADkYJHHQQoL6tKrW/LbmfR9ljz7ib2aWno4JO3VKQvLwjyUMPpq\n/SXXMOnx8jXwgWizpPxQYDRJ0SQXS9ULJ1hXRL/OgMnZAYvYDeV2jBnKsAIEdiG/\ne1qm8P4W9qpWJc+hNq7FOT13RzGWRx57SdLWSXo0KeY38r9lvjjOmT/cuOcmjwlD\nT9XYf/RSO+yJ/AsyMdAr+ZbDeQUd9HYJiPdI04lGaGM02MjDMnx+monc+y54t+Z+\nry1WtQdzoQt9dHlIPlV1tR+xV5DHHsejCZxu9TWzzSlL5wfBBeEz7R/OIzivGJpW\nQdJzd+2QDXSRg9q2XYWP5ZVtSgjVVJjNlb6ZABEBAAG0VEFydGlmYWN0IFJlZ2lz\ndHJ5IFJlcG9zaXRvcnkgU2lnbmVyIDxhcnRpZmFjdC1yZWdpc3RyeS1yZXBvc2l0\nb3J5LXNpZ25lckBnb29nbGUuY29tPokBTgQTAQoAOBYhBDW6oLM+nrOW9ZyoOMC6\nXObcYxWjBQJgkbezAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEMC6XObc\nYxWj+igIAMFh6DrAYMeq9sbZ1ZG6oAMrinUheGQbEqe76nIDQNsZnhDwZ2wWqgVC\n7DgOMqlhQmOmzm7M6Nzmq2dvPwq3xC2OeI9fQyzjT72deBTzLP7PJok9PJFOMdLf\nILSsUnmMsheQt4DUO0jYAX2KUuWOIXXJaZ319QyoRNBPYa5qz7qXS7wHLOY89IDq\nfHt6Aud8ER5zhyOyhytcYMeaGC1g1IKWmgewnhEq02FantMJGlmmFi2eA0EPD02G\nC3742QGqRxLwjWsm5/TpyuU24EYKRGCRm7QdVIo3ugFSetKrn0byOxWGBvtu4fH8\nXWvZkRT+u+yzH1s5yFYBqc2JTrrJvRU=\n=zwB2\n-----END PGP PUBLIC KEY BLOCK-----", + "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1\n\nmQENBFWKtqgBCADmKQWYQF9YoPxLEQZ5XA6DFVg9ZHG4HIuehsSJETMPQ+W9K5c5\nUs5assCZBjG/k5i62SmWb09eHtWsbbEgexURBWJ7IxA8kM3kpTo7bx+LqySDsSC3\n/8JRkiyibVV0dDNv/EzRQsGDxmk5Xl8SbQJ/C2ECSUT2ok225f079m2VJsUGHG+5\nRpyHHgoMaRNedYP8ksYBPSD6sA3Xqpsh/0cF4sm8QtmsxkBmCCIjBa0B0LybDtdX\nXIq5kPJsIrC2zvERIPm1ez/9FyGmZKEFnBGeFC45z5U//pHdB1z03dYKGrKdDpID\n17kNbC5wl24k/IeYyTY9IutMXvuNbVSXaVtRABEBAAG0Okdvb2dsZSBDbG91ZCBQ\nYWNrYWdlcyBSUE0gU2lnbmluZyBLZXkgPGdjLXRlYW1AZ29vZ2xlLmNvbT6JATgE\nEwECACIFAlWKtqgCGy8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPCcOUw+\nG6jV+QwH/0wRH+XovIwLGfkg6kYLEvNPvOIYNQWnrT6zZ+XcV47WkJ+i5SR+QpUI\nudMSWVf4nkv+XVHruxydafRIeocaXY0E8EuIHGBSB2KR3HxG6JbgUiWlCVRNt4Qd\n6udC6Ep7maKEIpO40M8UHRuKrp4iLGIhPm3ELGO6uc8rks8qOBMH4ozU+3PB9a0b\nGnPBEsZdOBI1phyftLyyuEvG8PeUYD+uzSx8jp9xbMg66gQRMP9XGzcCkD+b8w1o\n7v3J3juKKpgvx5Lqwvwv2ywqn/Wr5d5OBCHEw8KtU/tfxycz/oo6XUIshgEbS/+P\n6yKDuYhRp6qxrYXjmAszIT25cftb4d4=\n=/PbX\n-----END PGP PUBLIC KEY BLOCK-----" + ] + } + ] +} \ No newline at end of file diff --git a/tools/gen-manifests-diff b/tools/gen-manifests-diff index 4d9f86d7d0..6113c24a97 100755 --- a/tools/gen-manifests-diff +++ b/tools/gen-manifests-diff @@ -50,6 +50,7 @@ def run_gen_manifests(cmd, output_dir): "-containers=false", "-arches", ",".join(arches), "-output", output_dir, + "-buildconfig-allow-unknown", ], env=env, text=True, capture_output=True, check=False) if p.returncode != 0: print(p.stdout) @@ -100,6 +101,7 @@ def main(): print("Note that this diff does *not* include depsolved rpms,") print("so upstream dependency changes will not be caught\n") with tempfile.TemporaryDirectory() as tmpdir: + tmpdir = "/tmp" manifests_diff(pathlib.Path(tmpdir), rev)