From 1f852145fbc4613842983e659d5fb144c7d61a2c Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 13:09:47 +0200 Subject: [PATCH 01/14] distro: drop non-rhel10 tests from TestRH10DistroFactory This test used to test that the rhel distro factory does not match on non-rhel10 distro names. However there is no rhel10 specific distro anymore so these test-cases can get removed. --- pkg/distro/generic/rhel10_internal_test.go | 77 ---------------------- 1 file changed, 77 deletions(-) diff --git a/pkg/distro/generic/rhel10_internal_test.go b/pkg/distro/generic/rhel10_internal_test.go index 1bf4391f78..2d22ee7498 100644 --- a/pkg/distro/generic/rhel10_internal_test.go +++ b/pkg/distro/generic/rhel10_internal_test.go @@ -48,83 +48,6 @@ func TestRH10DistroFactory(t *testing.T) { strID: "centos-10", expected: common.Must(newDistro("centos-10")), }, - - { - strID: "rhel-90", - expected: nil, - }, - { - strID: "rhel-9.0", - expected: nil, - }, - { - strID: "rhel-93", - expected: nil, - }, - { - strID: "rhel-9.3", - expected: nil, - }, - { - strID: "rhel-910", - expected: nil, - }, - { - strID: "rhel-9.10", - expected: nil, - }, - { - strID: "centos-9", - expected: nil, - }, - { - strID: "centos-9.0", - expected: nil, - }, - { - strID: "rhel-9", - expected: nil, - }, - { - strID: "rhel-8.0", - expected: nil, - }, - { - strID: "rhel-80", - expected: nil, - }, - { - strID: "rhel-8.4", - expected: nil, - }, - { - strID: "rhel-84", - expected: nil, - }, - { - strID: "rhel-8.10", - expected: nil, - }, - { - strID: "rhel-810", - expected: nil, - }, - { - strID: "rhel-8", - expected: nil, - }, - { - strID: "rhel-8.4.1", - expected: nil, - }, - { - strID: "rhel-7", - expected: nil, - }, - { - strID: "rhel-79", - expected: nil, - }, } for _, tc := range testCases { From 3f700e880f75522da856b82ffd4f8321ad0036cd Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 13:12:33 +0200 Subject: [PATCH 02/14] distro: drop rhel-8.4 from TestDistroFactory --- pkg/distro/generic/fedora_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/distro/generic/fedora_test.go b/pkg/distro/generic/fedora_test.go index 80beb4fb28..dd4cd090b9 100644 --- a/pkg/distro/generic/fedora_test.go +++ b/pkg/distro/generic/fedora_test.go @@ -968,10 +968,6 @@ func TestFedoraDistroFactory(t *testing.T) { strID: "rhel-9", expected: nil, }, - { - strID: "rhel-8.4", - expected: nil, - }, { strID: "rhel-810", expected: nil, From 31621ab7b695e932da31b1e650e01c6de4071750 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 4 Jun 2025 17:12:40 +0200 Subject: [PATCH 03/14] distro: fix hardcoded "IoT" variant and dbus-broker We currently hardcode `IoT` in the generic images.go helpers, avoid this and get variant from from YAML. Likewise, do not hardcode adding dbus-broker, this is the wrong layer and needs to happen at the YAML layer. --- pkg/distro/defs/fedora/distro.yaml | 9 +++++++-- pkg/distro/generic/images.go | 11 +++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/distro/defs/fedora/distro.yaml b/pkg/distro/defs/fedora/distro.yaml index 86d2bf0a58..dddc3c63d1 100644 --- a/pkg/distro/defs/fedora/distro.yaml +++ b/pkg/distro/defs/fedora/distro.yaml @@ -175,6 +175,7 @@ - "org.fedoraproject.Anaconda.Modules.Users" additional_dracut_modules: - "net-lib" + - "dbus-broker" squashfs_rootfs: true conditions: "f41 dracut requires ifcfg, rootfs is squashfs": @@ -183,12 +184,14 @@ shallow_merge: additional_dracut_modules: - "ifcfg" + - "dbus-broker" "f40 and lower uses ifcfg in dracut and squashfs+ext4 rootfs": when: version_less_than: "41" shallow_merge: additional_dracut_modules: - "ifcfg" + - "dbus-broker" squashfs_rootfs: false image_config: @@ -944,7 +947,7 @@ image_types: bootable: true image_func: "iot" ostree: - name: "fedora" + name: "fedora-iot" remote: "fedora-iot" build_pipelines: ["build"] payload_pipelines: ["ostree-deployment", "image", "xz"] @@ -1023,7 +1026,7 @@ image_types: bootable: true image_func: "iot" ostree: - name: "fedora" + name: "fedora-iot" remote: "fedora-iot" build_pipelines: ["build"] payload_pipelines: ["ostree-deployment", "image", "qcow2"] @@ -1488,6 +1491,7 @@ image_types: boot_iso: true image_func: "iot_installer" iso_label: "IoT" + variant: "IoT" ostree: name: "fedora-iot" remote: "fedora-iot" @@ -1843,6 +1847,7 @@ image_types: default_size: 10_737_418_240 # 10 * datasizes.GibiByte image_func: "iot_simplified_installer" iso_label: "IoT" + variant: "IoT" ostree: name: "fedora" remote: "fedora-iot" diff --git a/pkg/distro/generic/images.go b/pkg/distro/generic/images.go index da40bf14fe..7b5b90e359 100644 --- a/pkg/distro/generic/images.go +++ b/pkg/distro/generic/images.go @@ -333,7 +333,7 @@ func ostreeDeploymentCustomizations( deploymentConf := manifest.OSTreeDeploymentCustomizations{} var kernelOptions []string - if len(t.defaultImageConfig.KernelOptions) > 0 { + if t.defaultImageConfig != nil && len(t.defaultImageConfig.KernelOptions) > 0 { kernelOptions = append(kernelOptions, t.defaultImageConfig.KernelOptions...) } if bpKernel := c.GetKernel(); bpKernel != nil && bpKernel.Append != "" { @@ -815,11 +815,8 @@ func iotInstallerImage(workload workload.Workload, } } - // On Fedora anaconda needs dbus-broker, but isn't added when dracut runs. - img.AdditionalDracutModules = append(img.AdditionalDracutModules, "dbus-broker") - img.Product = d.Product() - img.Variant = "IoT" + img.Variant = t.ImageTypeYAML.Variant img.OSVersion = d.OsVersion() img.Release = fmt.Sprintf("%s %s", d.Product(), d.OsVersion()) img.Preview = d.DistroYAML.Preview @@ -953,11 +950,9 @@ func iotSimplifiedInstallerImage(workload workload.Workload, img.AdditionalDrivers = append(img.AdditionalDrivers, installerConfig.AdditionalDrivers...) } - img.AdditionalDracutModules = append(img.AdditionalDracutModules, "dbus-broker") - d := t.arch.distro img.Product = d.Product() - img.Variant = "IoT" + img.Variant = t.ImageTypeYAML.Variant img.OSName = t.OSTree.Name img.OSVersion = d.OsVersion() From 78609b633c6a2a1db3b9d5eda2f08a42e3f835c7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 2 Jul 2025 12:06:54 +0200 Subject: [PATCH 04/14] loader: add support for platform conditionals On rhel-8 we change the platform based on the version, i.e ec2 images before rhel-8.9 are bios only. This needs a new conditional. Because it is an override and the order of conditionals is (currently) undefined (because its a go map) it will error if multiple conditions match. Thanks to Ondrej for this idea. --- pkg/distro/defs/loader.go | 64 +++++++++++++++++--- pkg/distro/defs/loader_test.go | 106 ++++++++++++++++++++++++++++++++- pkg/distro/generic/distro.go | 6 +- 3 files changed, 167 insertions(+), 9 deletions(-) diff --git a/pkg/distro/defs/loader.go b/pkg/distro/defs/loader.go index 8da08da9b2..86e4d33af1 100644 --- a/pkg/distro/defs/loader.go +++ b/pkg/distro/defs/loader.go @@ -370,7 +370,8 @@ type ImageTypeYAML struct { Exports []string `yaml:"exports"` RequiredPartitionSizes map[string]uint64 `yaml:"required_partition_sizes"` - Platforms []platform.PlatformConf `yaml:"platforms"` + InternalPlatforms []platform.PlatformConf `yaml:"platforms"` + PlatformsOverride *platformsOverride `yaml:"platforms_override"` NameAliases []string `yaml:"name_aliases"` @@ -389,6 +390,29 @@ func (it *ImageTypeYAML) Name() string { return it.name } +func (it *ImageTypeYAML) PlatformsFor(distroNameVer string) ([]platform.PlatformConf, error) { + pl := it.InternalPlatforms + if it.PlatformsOverride != nil { + id, err := distro.ParseID(distroNameVer) + if err != nil { + return nil, err + } + var nMatches int + for _, cond := range it.PlatformsOverride.Conditions { + // arch does not make sense for platform overrides + arch := "" + if cond.When.Eval(id, arch) { + pl = cond.Override + nMatches++ + } + } + if nMatches > 1 { + return nil, fmt.Errorf("platform conditionals for image type %q should match only once but matched %v times", it.Name(), nMatches) + } + } + return pl, nil +} + func (it *ImageTypeYAML) runTemplates(distro *DistroYAML) error { var data any // set the DistroVendor in the struct only if its actually @@ -401,22 +425,48 @@ func (it *ImageTypeYAML) runTemplates(distro *DistroYAML) error { DistroVendor: distro.Vendor, } } - for idx := range it.Platforms { - // fill the UEFI vendor string - templ, err := template.New("uefi-vendor").Parse(it.Platforms[idx].UEFIVendor) + subs := func(inp string) (string, error) { + templ, err := template.New("uefi-vendor").Parse(inp) templ.Option("missingkey=error") if err != nil { - return fmt.Errorf(`cannot parse template for "vendor" field: %w`, err) + return "", fmt.Errorf(`cannot parse template for "vendor" field: %w`, err) } var buf bytes.Buffer if err := templ.Execute(&buf, data); err != nil { - return fmt.Errorf(`cannot execute template for "vendor" field (is it set?): %w`, err) + return "", fmt.Errorf(`cannot execute template for "vendor" field (is it set?): %w`, err) + } + return buf.String(), nil + } + for idx := range it.InternalPlatforms { + newVendor, err := subs(it.InternalPlatforms[idx].UEFIVendor) + if err != nil { + return err + } + it.InternalPlatforms[idx].UEFIVendor = newVendor + } + if it.PlatformsOverride != nil { + for _, cond := range it.PlatformsOverride.Conditions { + for idx := range cond.Override { + newVendor, err := subs(cond.Override[idx].UEFIVendor) + if err != nil { + return err + } + cond.Override[idx].UEFIVendor = newVendor + } } - it.Platforms[idx].UEFIVendor = buf.String() } return nil } +type platformsOverride struct { + Conditions map[string]*conditionsPlatforms `yaml:"conditions,omitempty"` +} + +type conditionsPlatforms struct { + When whenCondition `yaml:"when,omitempty"` + Override []platform.PlatformConf `yaml:"override"` +} + type imageConfig struct { *distro.ImageConfig `yaml:",inline"` Conditions map[string]*conditionsImgConf `yaml:"conditions,omitempty"` diff --git a/pkg/distro/defs/loader_test.go b/pkg/distro/defs/loader_test.go index 65efff32a3..2dc6802c02 100644 --- a/pkg/distro/defs/loader_test.go +++ b/pkg/distro/defs/loader_test.go @@ -314,6 +314,10 @@ image_types: var fakeImageTypesYaml = ` image_types: test_type: + filename: "disk.img" + image_func: "disk" + platforms: + - arch: x86_64 partition_table: test_arch: &test_arch_pt size: 1_000_000_000 @@ -704,7 +708,7 @@ distros: QCOW2Compat: "1.1", UEFIVendor: "test-vendor", }, - }, imgType.Platforms) + }, imgType.InternalPlatforms) } func TestImageTypesUEFIVendorErrorWhenEmpty(t *testing.T) { @@ -1095,3 +1099,103 @@ func TestWhenConditionEvalAnd(t *testing.T) { assert.Equal(t, wc.Eval(&distro.ID{Name: "distro"}, "other-arch"), false) assert.Equal(t, wc.Eval(&distro.ID{Name: "distro"}, "arch"), true) } + +func TestImageTypesPlatformOverrides(t *testing.T) { + fakeImageTypesYaml := ` +image_types: + server-qcow2: + filename: "disk.qcow2" + exports: ["qcow2"] + platforms_override: + conditions: + "test platform override, simulate old distro is bios only": + when: + version_less_than: "2" + override: + - arch: x86_64 + # note no uefi_vendor here + platforms: + - arch: x86_64 + uefi_vendor: "some-uefi-vendor" +` + + fakeDistrosYAML := ` +distros: + - name: test-distro-1 + vendor: test-vendor + defs_path: test-distro/ + - name: test-distro-2 + vendor: test-vendor + defs_path: test-distro/ +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYAML, fakeImageTypesYaml) + restore := defs.MockDataFS(baseDir) + defer restore() + + for _, tc := range []struct { + distroNameVer string + expectedUEFIVendor string + }{ + {"test-distro-1", ""}, + {"test-distro-2", "some-uefi-vendor"}, + } { + + distro, err := defs.NewDistroYAML(tc.distroNameVer) + require.NoError(t, err) + + imgTypes := distro.ImageTypes() + assert.Len(t, imgTypes, 1) + imgType := imgTypes["server-qcow2"] + platforms, err := imgType.PlatformsFor(tc.distroNameVer) + assert.NoError(t, err) + assert.Equal(t, []platform.PlatformConf{ + { + Arch: arch.ARCH_X86_64, + UEFIVendor: tc.expectedUEFIVendor, + }, + }, platforms) + } +} + +func TestImageTypesPlatformOverridesMultiMarchError(t *testing.T) { + fakeImageTypesYaml := ` +image_types: + server-qcow2: + filename: "disk.qcow2" + exports: ["qcow2"] + platforms: + - arch: x86_64 + platforms_override: + conditions: + "this is true": + when: + version_less_than: "2" + override: + - arch: x86_64 + uefi_vendor: "uefi-for-ver-2" + "this is also true": + when: + version_less_than: "3" + override: + - arch: x86_64 + uefi_vendor: "uefi-for-ver-3" +` + + fakeDistrosYAML := ` +distros: + - name: test-distro-1 + vendor: test-vendor + defs_path: test-distro/ +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYAML, fakeImageTypesYaml) + restore := defs.MockDataFS(baseDir) + defer restore() + + distro, err := defs.NewDistroYAML("test-distro-1") + assert.NoError(t, err) + imgTypes := distro.ImageTypes() + assert.Len(t, imgTypes, 1) + imgType := imgTypes["server-qcow2"] + _, err = imgType.PlatformsFor("test-distro-1") + assert.EqualError(t, err, `platform conditionals for image type "server-qcow2" should match only once but matched 2 times`) +} diff --git a/pkg/distro/generic/distro.go b/pkg/distro/generic/distro.go index 3ce8ae6514..93f4c7d100 100644 --- a/pkg/distro/generic/distro.go +++ b/pkg/distro/generic/distro.go @@ -96,7 +96,11 @@ func newDistro(nameVer string) (distro.Distro, error) { if imgTypeYAML.Filename == "" { continue } - for _, pl := range imgTypeYAML.Platforms { + platforms, err := imgTypeYAML.PlatformsFor(nameVer) + if err != nil { + return nil, err + } + for _, pl := range platforms { ar, ok := rd.arches[pl.Arch.String()] if !ok { ar = newArchitecture(rd, pl.Arch.String()) From f644c180ba27581976570f9bff54bddb30a89343 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 4 Jun 2025 16:20:55 +0200 Subject: [PATCH 05/14] distro: move rhel8 to the generic distro This commit move rhel8 into a generic distro. --- pkg/distro/defs/distros.yaml | 55 +++- pkg/distro/defs/fedora/distro.yaml | 14 +- pkg/distro/defs/loader.go | 14 +- pkg/distro/defs/rhel-8/distro.yaml | 428 ++++++++++++++++++++++++++++- pkg/distro/generic/images.go | 33 ++- pkg/distro/generic/imagetype.go | 4 +- pkg/distro/generic/options.go | 187 +++++++++++++ pkg/distrofactory/distrofactory.go | 2 - 8 files changed, 714 insertions(+), 23 deletions(-) diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 06b82b742b..4e99aa6641 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -93,10 +93,9 @@ distros: - ec2 - ec2-sap - ec2-ha - # rhel9 & cs9 share the same list - # of allowed profiles so a single - # allow list can be used - oscap_profiles_allowlist: &oscap_profile_allowlist_rhel10 + # rhel & centos share the same list of allowed profiles so a + # single allow list can be used + oscap_profiles_allowlist: &oscap_profile_allowlist_rhel - "xccdf_org.ssgproject.content_profile_anssi_bp28_enhanced" - "xccdf_org.ssgproject.content_profile_anssi_bp28_high" - "xccdf_org.ssgproject.content_profile_anssi_bp28_intermediary" @@ -138,7 +137,7 @@ distros: runner: name: org.osbuild.centos10 build_packages: *rhel10_runner_build_packages - oscap_profiles_allowlist: *oscap_profile_allowlist_rhel10 + oscap_profiles_allowlist: *oscap_profile_allowlist_rhel - <<: *centos10 name: "almalinux_kitten-10" @@ -149,6 +148,52 @@ distros: vendor: "almalinux" ostree_ref_tmpl: "almalinux/10/%s/edge" + - &rhel8 + name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" + match: "rhel-8.[0-9]{,[0-9]}" + distro_like: rhel-8 + product: "Red Hat Enterprise Linux" + os_version: "8.{{.MinorVersion}}" + release_version: 8 + module_platform_id: "platform:el8" + vendor: "redhat" + ostree_ref_tmpl: "rhel/8/%s/edge" + default_fs_type: "xfs" + defs_path: rhel-8 + iso_label_tmpl: "RHEL-{{.Distro.MajorVersion}}-{{.Distro.MinorVersion}}-0-BaseOS-{{.Arch}}" + conditions: + "edge image types require FDO which aren't available on older versions": + when: + version_less_than: "8.6" + ignore_image_types: + - "edge-simplified-installer" + - "edge-raw-image" + "azure eap7 is only available for rhel8.6+": + when: + version_less_than: "8.6" + ignore_image_types: + - "azure-eap7-rhui" + "Azure image types require hyperv-daemons which isn't available on older versions": + when: + version_less_than: "8.6" + arch: "aarch64" + ignore_image_types: + - "azure-rhui" + - "vhd" + runner: + name: "org.osbuild.rhel{{.MajorVersion}}{{.MinorVersion}}" + build_packages: &rhel_runner_build_packages + - "glibc" # ldconfig + - "systemd" # systemd-tmpfiles and systemd-sysusers + - "platform-python" # osbuild + # The RHEL 8 runner in osbuild runs with platform-python but + # explicitly symlinks python 3.6 to /etc/alternatives (which in turn + # is the target for /usr/bin/python3) for the stages. + # https://github.com/osbuild/osbuild/blob/ea8261cad6c5c606c00c0f2824c3f483c01a0cc9/runners/org.osbuild.rhel82#L61 + # Install python36 explicitly for RHEL 8. + - "python36" + oscap_profiles_allowlist: *oscap_profile_allowlist_rhel + - &rhel7 name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" match: "rhel-7.[0-9]{,[0-9]}" diff --git a/pkg/distro/defs/fedora/distro.yaml b/pkg/distro/defs/fedora/distro.yaml index dddc3c63d1..5fa62638e1 100644 --- a/pkg/distro/defs/fedora/distro.yaml +++ b/pkg/distro/defs/fedora/distro.yaml @@ -773,8 +773,9 @@ image_types: payload_pipelines: ["os", "ostree-commit", "commit-archive"] exports: ["commit-archive"] required_partition_sizes: *default_required_dir_sizes - image_config: + image_config: &image_config_iot_commit <<: *image_config_iot_enabled_services + install_weak_deps: false dracut_conf: - filename: "40-fips.conf" config: @@ -933,6 +934,9 @@ image_types: payload_pipelines: ["os", "ostree-commit", "container-tree", "container"] exports: ["container"] required_partition_sizes: *default_required_dir_sizes + image_config: + <<: *image_config_iot_commit + install_weak_deps: true platforms: - *x86_64_installer_platform - *aarch64_installer_platform @@ -948,7 +952,7 @@ image_types: image_func: "iot" ostree: name: "fedora-iot" - remote: "fedora-iot" + remote_name: "fedora-iot" build_pipelines: ["build"] payload_pipelines: ["ostree-deployment", "image", "xz"] exports: ["xz"] @@ -1027,7 +1031,7 @@ image_types: image_func: "iot" ostree: name: "fedora-iot" - remote: "fedora-iot" + remote_name: "fedora-iot" build_pipelines: ["build"] payload_pipelines: ["ostree-deployment", "image", "qcow2"] exports: ["qcow2"] @@ -1494,7 +1498,7 @@ image_types: variant: "IoT" ostree: name: "fedora-iot" - remote: "fedora-iot" + remote_name: "fedora-iot" build_pipelines: ["build"] payload_pipelines: - "anaconda-tree" @@ -1850,7 +1854,7 @@ image_types: variant: "IoT" ostree: name: "fedora" - remote: "fedora-iot" + remote_name: "fedora-iot" build_pipelines: ["build"] payload_pipelines: - "ostree-deployment" diff --git a/pkg/distro/defs/loader.go b/pkg/distro/defs/loader.go index 86e4d33af1..1e129def88 100644 --- a/pkg/distro/defs/loader.go +++ b/pkg/distro/defs/loader.go @@ -350,7 +350,11 @@ type ImageTypeYAML struct { Environment environment.EnvironmentConf `yaml:"environment"` Bootable bool `yaml:"bootable"` - BootISO bool `yaml:"boot_iso"` + BootISO bool `yaml:"boot_iso"` + // XXX merge with BootISO above, controls if grub2 or syslinux are used for ISO boots + UseSyslinux bool `yaml:"use_syslinux"` + UseLegacyAnacondaConfig bool `yaml:"use_legacy_anaconda_config"` + ISOLabel string `yaml:"iso_label"` // XXX: or iso_variant? Variant string `yaml:"variant"` @@ -358,9 +362,11 @@ type ImageTypeYAML struct { RPMOSTree bool `yaml:"rpm_ostree"` OSTree struct { - Name string `yaml:"name"` - Remote string `yaml:"remote"` + Name string `yaml:"name"` + RemoteName string `yaml:"remote_name"` } `yaml:"ostree"` + // XXX: rhel-8 uses this + UseOstreeRemotes bool `yaml:"use_ostree_remotes"` DefaultSize uint64 `yaml:"default_size"` // the image func name: disk,container,live-installer,... @@ -375,6 +381,8 @@ type ImageTypeYAML struct { NameAliases []string `yaml:"name_aliases"` + InstallWeakDeps *bool `yaml:"install_weak_deps"` + // for RHEL7 compat // TODO: determine a better place for these options, but for now they are here DiskImagePartTool *osbuild.PartTool `yaml:"disk_image_part_tool"` diff --git a/pkg/distro/defs/rhel-8/distro.yaml b/pkg/distro/defs/rhel-8/distro.yaml index 6bb78eaf78..9630e7dcff 100644 --- a/pkg/distro/defs/rhel-8/distro.yaml +++ b/pkg/distro/defs/rhel-8/distro.yaml @@ -765,6 +765,104 @@ - "insights-client" - "subscription-manager-cockpit" + 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 + + platforms: + x86_64_uefi_platform: &x86_64_uefi_platform + arch: "x86_64" + bootloader: "grub2" + uefi_vendor: "{{.DistroVendor}}" + packages: &x86_64_uefi_platform_packages + uefi: + - "dracut-config-generic" + - "efibootmgr" + - "grub2-efi-x64" + - "shim-x64" + x86_64_bios_platform: &x86_64_bios_platform + <<: *x86_64_uefi_platform + bios_platform: "i386-pc" + uefi_vendor: "{{.DistroVendor}}" + packages: &x86_64_bios_platform_packages + <<: *x86_64_uefi_platform_packages + bios: + - "dracut-config-generic" + - "grub2-pc" + build_packages: + bios: + - "grub2-pc" + x86_64_bios_only_platform: &x86_64_bios_only_platform + arch: "x86_64" + bios_platform: "i386-pc" + bootloader: "grub2" + image_format: "raw" + packages: + bios: + - "grub2-pc" + build_packages: + bios: + - "grub2-pc" + # XXX: rename to bareMetalX86Platform? + x86_64_installer_platform: &x86_64_installer_platform + <<: *x86_64_bios_platform + image_format: "raw" + packages: + <<: *x86_64_bios_platform_packages + firmware: + - "microcode_ctl" # ?? + - "iwl1000-firmware" + - "iwl100-firmware" + - "iwl105-firmware" + - "iwl135-firmware" + - "iwl2000-firmware" + - "iwl2030-firmware" + - "iwl3160-firmware" + - "iwl5000-firmware" + - "iwl5150-firmware" + - "iwl6050-firmware" + aarch64_platform: &aarch64_platform + arch: "aarch64" + bootloader: "grub2" + uefi_vendor: "{{.DistroVendor}}" + image_format: "qcow2" + packages: &aarch64_uefi_platform_packages + uefi: + - "dracut-config-generic" + - "efibootmgr" + - "grub2-efi-aa64" + - "grub2-tools" + - "shim-aa64" + ppc64le_bios_platform: &ppc64le_bios_platform + arch: "ppc64le" + bootloader: "grub2" + bios_platform: "powerpc-ieee1275" + image_format: "qcow2" + packages: + bios: + - "dracut-config-generic" + - "powerpc-utils" + - "grub2-ppc64le" + - "grub2-ppc64le-modules" + build_packages: + bios: + - "grub2-ppc64le" + - "grub2-ppc64le-modules" + s390x_zipl_platform: &s390x_zipl_platform + arch: "s390x" + bootloader: "zipl" + zipl_support: true + image_format: "qcow2" + packages: + zipl: + - "dracut-config-generic" + - "s390utils-base" + - "s390utils-core" + build_packages: + zipl: + - "s390utils-base" + partitioning: ids: - &prep_partition_dosid "41" @@ -1000,7 +1098,6 @@ - *ec2_partition_table_part_root aarch64: <<: *ec2_partition_table_aarch64_8_9 - azure_rhui_partition_tables: &azure_rhui_partition_tables x86_64: uuid: "D209C89E-EA5E-4FBD-B161-B461CCE297E0" @@ -1165,6 +1262,34 @@ image_types: <<: *condition_rhel_insights_clinet_subman ami: &ami + filename: "image.raw" + mime_type: "application/octet-stream" + image_func: "disk" + build_pipelines: ["build"] + payload_pipelines: ["os", "image"] + exports: ["image"] + bootable: true + default_size: 10_737_418_240 # 10 * datasizes.GibiByte + required_partition_sizes: *default_required_dir_sizes + platforms: + - <<: *x86_64_bios_platform + image_format: "raw" + - <<: *aarch64_platform + image_format: "raw" + platforms_override: + conditions: &platform_override_conditions_ami + "Keep the RHEL EC2 x86_64 images before 8.9 BIOS-only": + when: + # Keep the RHEL EC2 x86_64 images before 8.9 BIOS-only for backward compatibility. + # RHEL-internal EC2 images and RHEL AMI images are kept intentionally in sync + # with regard to not supporting hybrid boot mode before RHEL version 8.9. + # The partitioning table for these reflects that and is also intentionally in sync. + distro_name: "rhel" + version_less_than: "8.9" + override: + - *x86_64_bios_only_platform + - <<: *aarch64_platform + image_format: "raw" package_sets: os: - *ec2_common_pkgset @@ -1176,6 +1301,11 @@ image_types: ec2: &ec2 <<: *ami + filename: "image.raw.xz" + mime_type: "application/xz" + compression: "xz" + payload_pipelines: ["os", "image", "xz"] + exports: ["xz"] image_config: <<: *ec2_image_config package_sets: @@ -1195,6 +1325,19 @@ image_types: "ec2-ha": <<: *ec2 + platforms: + - <<: *x86_64_bios_platform + image_format: "raw" + platforms_override: + conditions: &platform_override_conditions_ami + # XXX: duplicated with "ami" but drops "aarch64", I cannot + # find a better way to express this complex condition in YAML + "Keep the RHEL EC2 x86_64 images before 8.9 BIOS-only": + when: + distro_name: "rhel" + version_less_than: "8.9" + override: + - *x86_64_bios_only_platform package_sets: os: - *ec2_common_pkgset @@ -1210,6 +1353,19 @@ image_types: "ec2-sap": <<: *ec2 + platforms: + - <<: *x86_64_bios_platform + image_format: "raw" + platforms_override: + conditions: &platform_override_conditions_ami + # XXX: duplicated with "ami" but drops "aarch64", I cannot + # find a better way to express this complex condition in YAML + "Keep the RHEL EC2 x86_64 images before 8.9 BIOS-only": + when: + distro_name: "rhel" + version_less_than: "8.9" + override: + - *x86_64_bios_only_platform image_config: <<: [*ec2_image_config, *sap_image_config] conditions: @@ -1251,6 +1407,30 @@ image_types: - "rh-amazon-rhui-client-sap-bundle" qcow2: &qcow2 + filename: "disk.qcow2" + mime_type: "application/x-qemu-disk" + # note that unlike fedora rhel does not use the environment.KVM + # for unknown reasons + bootable: true + default_size: 10_737_418_240 # 10 * datasizes.GibiByte + image_func: "disk" + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "qcow2"] + exports: ["qcow2"] + required_partition_sizes: *default_required_dir_sizes + platforms: + - <<: *x86_64_bios_platform + image_format: "qcow2" + qcow2_compat: "0.10" + - <<: *aarch64_platform + image_format: "qcow2" + qcow2_compat: "0.10" + - <<: *ppc64le_bios_platform + image_format: "qcow2" + qcow2_compat: "0.10" + - <<: *s390x_zipl_platform + image_format: "qcow2" + qcow2_compat: "0.10" image_config: default_target: "multi-user.target" kernel_options: @@ -1278,6 +1458,21 @@ image_types: - *qcow2_common_pkgset vhd: + filename: "disk.vhd" + mime_type: "application/x-vhd" + image_func: "disk" + default_size: 4_294_967_296 # 4 * datasizes.GibiByte + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "vpc"] + exports: ["vpc"] + bootable: true + # note that unlike fedora no "environment.Azure" is used here for + # unknown reasons + platforms: + - <<: *x86_64_bios_platform + image_format: "vhd" + - <<: *aarch64_platform + image_format: "vhd" # yamllint disable rule:line-length # based on https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/deploying_rhel_8_on_microsoft_azure/assembly_deploying-a-rhel-image-as-a-virtual-machine-on-microsoft-azure_cloud-content-azure#making-configuration-changes_configure-the-image-azure # yamllint enable rule:line-length @@ -1424,6 +1619,20 @@ image_types: - "alsa-lib" "azure-rhui": &azure_rhui + filename: "disk.vhd.xz" + mime_type: "application/xz" + image_func: "disk" + default_size: 68_719_476_736 # 64 * datasizes.GibiByte + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "vpc", "xz"] + exports: ["xz"] + compression: "xz" + bootable: true + platforms: + - <<: *x86_64_bios_platform + image_format: "vhd" + - <<: *aarch64_platform + image_format: "vhd" image_config: &azure_rhui_image_config <<: *vhd_image_config rhsm_config: @@ -1463,6 +1672,9 @@ image_types: "azure-sap-rhui": <<: *azure_rhui + platforms: + - <<: *x86_64_bios_platform + image_format: "vhd" image_config: <<: [*azure_rhui_image_config, *sap_image_config] conditions: @@ -1489,6 +1701,9 @@ image_types: "azure-eap7-rhui": <<: *azure_rhui + platforms: + - <<: *x86_64_bios_platform + image_format: "vhd" image_config: <<: *azure_rhui_image_config enabled_services: @@ -1556,13 +1771,42 @@ image_types: - "python3-html5lib" "image-installer": + filename: "installer.iso" + mime_type: "application/x-iso9660-image" + bootable: true + boot_iso: true + # XXX: merge with boot_iso above + use_syslinux: true + # NOTE: RHEL 8 only supports the older Anaconda configs + use_legacy_anaconda_config: true + image_func: "image_installer" + # We don't know the variant of the OS pipeline being installed + iso_label: "Unknown" + build_pipelines: ["build"] + payload_pipelines: + - "anaconda-tree" + - "efiboot-tree" + - "os" + - "bootiso-tree" + - "bootiso" + exports: ["bootiso"] + required_partition_sizes: *default_required_dir_sizes image_config: + iso_rootfs_type: "squashfs-ext4" conditions: "x86_64 uses syslinux": when: arch: "x86_64" shallow_merge: iso_boot_type: "syslinux" + installer_config: + # see commit c6bfb22f54, controls the kickstart location + iso_root_kickstart: true + additional_dracut_modules: + - "ifcfg" + platforms: + - *x86_64_installer_platform + - *aarch64_platform package_sets: <<: *bare_metal_pkgset installer: @@ -1591,7 +1835,19 @@ image_types: - "rng-tools" "edge-commit": &edge_commit + name_aliases: ["rhel-edge-commit"] + filename: "commit.tar" + mime_type: "application/x-tar" + rpm_ostree: true + image_func: "iot_commit" + build_pipelines: ["build"] + payload_pipelines: ["os", "ostree-commit", "commit-archive"] + exports: ["commit-archive"] + platforms: + - *x86_64_bios_platform + - *aarch64_platform image_config: &edge_commit_image_config + install_weak_deps: true enabled_services: &enabled_services_edge - "NetworkManager.service" - "firewalld.service" @@ -1758,6 +2014,28 @@ image_types: - "sos" "edge-installer": + name_aliases: ["rhel-edge-installer"] + filename: "installer.iso" + mime_type: "application/x-iso9660-image" + rpm_ostree: true + boot_iso: true + # XXX: merge with boot_iso above + use_syslinux: true + # NOTE: RHEL 8 only supports the older Anaconda configs + use_legacy_anaconda_config: true + image_func: "iot_installer" + iso_label: "edge" + variant: "edge" + ostree: + name: "rhel-edge" + build_pipelines: ["build"] + payload_pipelines: + - "anaconda-tree" + - "efiboot-tree" + - "bootiso-tree" + - "bootiso" + exports: ["bootiso"] + required_partition_sizes: *default_required_dir_sizes image_config: iso_rootfs_type: "squashfs-ext4" conditions: @@ -1766,6 +2044,12 @@ image_types: arch: "x86_64" shallow_merge: iso_boot_type: "syslinux" + installer_config: + additional_dracut_modules: + - "ifcfg" + platforms: + - *x86_64_uefi_platform + - *aarch64_platform package_sets: installer: # TODO: non-arch-specific package set handling for installers @@ -1781,6 +2065,26 @@ image_types: # XXX: only available for rhel-8.6+, this is not possible to limit right now "edge-raw-image": + name_aliases: ["rhel-edge-raw-image"] + filename: "image.raw.xz" + compression: "xz" + mime_type: "application/xz" + default_size: 10_737_418_240 # 10 * datasizes.GibiByte + rpm_ostree: true + bootable: true + image_func: "iot" + ostree: + name: "rhel-edge" + remote_name: "rhel-edge" + use_ostree_remotes: true + build_pipelines: ["build"] + payload_pipelines: ["ostree-deployment", "image", "xz"] + exports: ["xz"] + platforms: + - <<: *x86_64_installer_platform + image_format: "raw" + - <<: *aarch64_platform + image_format: "raw" image_config: keyboard: keymap: "us" @@ -1789,10 +2093,52 @@ image_types: kernel_options: ["modprobe.blacklist=vc4"] partition_table: <<: *edge_base_partition_tables + supported_partitioning_modes: + - "" # default partitioning mode is supported (in original go based code) + - "raw" "edge-simplified-installer": + filename: "simplified-installer.iso" + mime_type: "application/x-iso9660-image" + rpm_ostree: true + bootable: true + boot_iso: true + # XXX: merge with boot_iso above + use_syslinux: true + # NOTE: RHEL 8 only supports the older Anaconda configs + use_legacy_anaconda_config: true + default_size: 10_737_418_240 # 10 * datasizes.GibiByte + image_func: "iot_simplified_installer" + iso_label: "edge" + variant: "edge" + ostree: + name: "rhel-edge" + remote_name: "rhel-edge" + # XXX: find better name + use_ostree_remotes: true + build_pipelines: ["build"] + payload_pipelines: + - "ostree-deployment" + - "image" + - "xz" + - "coi-tree" + - "efiboot-tree" + - "bootiso-tree" + - "bootiso" + exports: ["bootiso"] + required_partition_sizes: *default_required_dir_sizes + installer_config: + additional_dracut_modules: + - "prefixdevname" + - "prefixdevname-tools" + platforms: + - <<: *x86_64_uefi_platform + - <<: *aarch64_platform partition_table: <<: *edge_base_partition_tables + supported_partitioning_modes: + - "" # default partitioning mode is supported (in original go based code) + - "raw" image_config: enabled_services: *enabled_services_edge keyboard: @@ -1862,6 +2208,18 @@ image_types: <<: *edge_commit_aarch64_pkgset "edge-container": + name_aliases: ["rhel-edge-container"] + filename: "container.tar" + mime_type: "application/x-tar" + rpm_ostree: true + image_func: "iot_container" + build_pipelines: ["build"] + payload_pipelines: ["os", "ostree-commit", "container-tree", "container"] + exports: ["container"] + required_partition_sizes: *default_required_dir_sizes + platforms: + - *x86_64_bios_platform + - *aarch64_platform image_config: <<: *edge_commit_image_config package_sets: @@ -1869,6 +2227,18 @@ image_types: - *edge_commit_pkgset vmdk: &vmdk + filename: "disk.vmdk" + mime_type: "application/x-vmdk" + bootable: true + default_size: 4_294_967_296 # 4 * datasizes.GibiByte + image_func: "disk" + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "vmdk"] + exports: ["vmdk"] + required_partition_sizes: *default_required_dir_sizes + platforms: + - <<: *x86_64_bios_platform + image_format: "vmdk" image_config: kernel_options: - "ro" @@ -1891,12 +2261,28 @@ image_types: ova: <<: *vmdk + filename: "image.ova" + mime_type: "application/ovf" + payload_pipelines: ["os", "image", "vmdk", "ovf", "archive"] + exports: ["archive"] + platforms: + - <<: *x86_64_bios_platform + image_format: "ova" gce: &gce + filename: "image.tar.gz" + mime_type: "application/gzip" + image_func: "disk" + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "archive"] + exports: ["archive"] + bootable: true + default_size: 21_474_836_480 # 20 * datasizes.GibiByte # The configuration for non-RHUI images does not touch the RHSM configuration at all. # https://issues.redhat.com/browse/COMPOSER-2157 image_config: &gce_image_config timezone: "UTC" + kernel_options: ["net.ifnames=0", "biosdevname=0", "scsi_mod.use_blk_mq=Y", "crashkernel=auto", "console=ttyS0,38400n8d"] time_synchronization: servers: - hostname: "metadata.google.internal" @@ -1959,7 +2345,6 @@ image_types: config: "InstanceSetup": set_boto_config: false - kernel_options: ["net.ifnames=0", "biosdevname=0", "scsi_mod.use_blk_mq=Y", "crashkernel=auto", "console=ttyS0,38400n8d"] conditions: "rhel-8.4 needs special handling": # NOTE(akoutsou): these are enabled in the package preset, but for @@ -1977,6 +2362,9 @@ image_types: - "google-shutdown-scripts.service" - "google-startup-scripts.service" - "google-osconfig-agent.service" + platforms: + - <<: *x86_64_uefi_platform + image_format: "gce" partition_table: <<: *default_partition_tables package_sets: @@ -2008,14 +2396,25 @@ image_types: oci: <<: *qcow2 + platforms: + - <<: *x86_64_bios_platform + image_format: "qcow2" + qcow2_compat: "0.10" openstack: <<: *qcow2 + default_size: 4_294_967_296 # 4 * datasizes.GibiByte image_config: kernel_options: - "ro" - "net.ifnames=0" platforms: + - <<: *x86_64_bios_platform + image_format: "qcow2" + - <<: *aarch64_platform + image_format: "qcow2" + partition_table: + <<: *default_partition_tables package_sets: os: - include: @@ -2031,6 +2430,16 @@ image_types: - "rng-tools" wsl: + filename: "image.wsl" + mime_type: "application/x-tar" + image_func: "tar" + build_pipelines: ["build"] + payload_pipelines: ["os", "archive", "xz"] + exports: ["xz"] + compression: "xz" + platforms: + - arch: "x86_64" + - arch: "aarch64" image_config: no_selinux: true wsl: @@ -2144,6 +2553,16 @@ image_types: - "xz" "minimal-raw": + filename: "disk.raw.xz" + mime_type: "application/xz" + compression: "xz" + bootable: true + default_size: 2_147_483_648 # 2 * datasizes.GibiByte + image_func: "disk" + build_pipelines: ["build"] + payload_pipelines: ["os", "image", "xz"] + exports: ["xz"] + required_partition_sizes: *default_required_dir_sizes image_config: enabled_services: - "NetworkManager.service" @@ -2162,6 +2581,11 @@ image_types: firstboot --reconfig lang en_US.UTF-8 kernel_options: ["ro"] + platforms: + - <<: *x86_64_uefi_platform + image_format: "raw" + - <<: *aarch64_platform + image_format: "raw" partition_table: <<: *default_partition_tables package_sets: diff --git a/pkg/distro/generic/images.go b/pkg/distro/generic/images.go index 7b5b90e359..a987420ce8 100644 --- a/pkg/distro/generic/images.go +++ b/pkg/distro/generic/images.go @@ -571,6 +571,8 @@ func imageInstallerImage(workload workload.Workload, return nil, err } + img.UseLegacyAnacondaConfig = t.ImageTypeYAML.UseLegacyAnacondaConfig + img.Kickstart, err = kickstart.New(customizations) if err != nil { return nil, err @@ -666,9 +668,13 @@ func iotCommitImage(workload workload.Workload, if err != nil { return nil, err } + imgConfig := t.getDefaultImageConfig() if imgConfig != nil { img.OSCustomizations.Presets = imgConfig.Presets + if imgConfig.InstallWeakDeps != nil { + img.InstallWeakDeps = *imgConfig.InstallWeakDeps + } } img.Environment = &t.ImageTypeYAML.Environment @@ -676,7 +682,6 @@ func iotCommitImage(workload workload.Workload, img.OSTreeParent = parentCommit img.OSVersion = d.OsVersion() img.Filename = t.Filename() - img.InstallWeakDeps = false return img, nil } @@ -743,6 +748,9 @@ func iotContainerImage(workload workload.Workload, imgConfig := t.getDefaultImageConfig() if imgConfig != nil { img.OSCustomizations.Presets = imgConfig.Presets + if imgConfig.InstallWeakDeps != nil { + img.OSCustomizations.InstallWeakDeps = *imgConfig.InstallWeakDeps + } } img.ContainerLanguage = img.OSCustomizations.Language @@ -777,6 +785,7 @@ func iotInstallerImage(workload workload.Workload, img.FIPS = customizations.GetFIPS() img.Platform = t.platform img.ExtraBasePackages = packageSets[installerPkgsKey] + img.UseLegacyAnacondaConfig = t.ImageTypeYAML.UseLegacyAnacondaConfig img.Kickstart, err = kickstart.New(customizations) if err != nil { @@ -784,7 +793,7 @@ func iotInstallerImage(workload workload.Workload, } img.Kickstart.OSTree = &kickstart.OSTree{ OSName: t.OSTree.Name, - Remote: t.OSTree.Remote, + Remote: t.OSTree.RemoteName, } img.Kickstart.Path = osbuild.KickstartPathOSBuild img.Kickstart.Language, img.Kickstart.Keyboard = customizations.GetPrimaryLocale() @@ -814,6 +823,10 @@ func iotInstallerImage(workload workload.Workload, img.RootfsType = manifest.SquashfsRootfs } } + if len(img.Kickstart.Users)+len(img.Kickstart.Groups) > 0 { + // only enable the users module if needed + img.AdditionalAnacondaModules = append(img.AdditionalAnacondaModules, anaconda.ModuleUsers) + } img.Product = d.Product() img.Variant = t.ImageTypeYAML.Variant @@ -867,9 +880,15 @@ func iotImage(workload workload.Workload, img.Workload = workload img.Remote = ostree.Remote{ - Name: t.OSTree.Remote, + Name: t.ImageTypeYAML.OSTree.RemoteName, + } + // XXX: can we do better? + if t.ImageTypeYAML.UseOstreeRemotes { + img.Remote.URL = options.OSTree.URL + img.Remote.ContentURL = options.OSTree.ContentURL } - img.OSName = t.OSTree.Remote + + img.OSName = t.ImageTypeYAML.OSTree.Name // TODO: move generation into LiveImage pt, err := t.getPartitionTable(customizations, options, rng) @@ -908,7 +927,11 @@ func iotSimplifiedInstallerImage(workload workload.Workload, rawImg.Platform = t.platform rawImg.Workload = workload rawImg.Remote = ostree.Remote{ - Name: t.OSTree.Remote, + Name: t.OSTree.RemoteName, + } + if t.ImageTypeYAML.UseOstreeRemotes { + rawImg.Remote.URL = options.OSTree.URL + rawImg.Remote.ContentURL = options.OSTree.ContentURL } rawImg.OSName = t.OSTree.Name diff --git a/pkg/distro/generic/imagetype.go b/pkg/distro/generic/imagetype.go index 8fc7317888..087ee03cbf 100644 --- a/pkg/distro/generic/imagetype.go +++ b/pkg/distro/generic/imagetype.go @@ -319,7 +319,9 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp return checkOptionsFedora(t, bp, options) case manifest.DISTRO_EL7: return checkOptionsRhel7(t, bp, options) - // TODO: add checkOptionsRhel{8,9} once we move them to + case manifest.DISTRO_EL8: + return checkOptionsRhel8(t, bp, options) + // TODO: add checkOptionsRhel9 once we move them to // generic distros case manifest.DISTRO_EL10: return checkOptionsRhel10(t, bp, options) diff --git a/pkg/distro/generic/options.go b/pkg/distro/generic/options.go index c2c33b1226..a16f2a5efb 100644 --- a/pkg/distro/generic/options.go +++ b/pkg/distro/generic/options.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/osbuild/images/internal/common" + "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/blueprint" "github.com/osbuild/images/pkg/customizations/oscap" "github.com/osbuild/images/pkg/distro" @@ -94,6 +95,192 @@ func checkOptionsRhel10(t *imageType, bp *blueprint.Blueprint, options distro.Im return warnings, nil } +func checkOptionsRhel8(t *imageType, bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) { + customizations := bp.Customizations + // holds warnings (e.g. deprecation notices) + var warnings []string + + // we do not support embedding containers on ostree-derived images, only on commits themselves + if len(bp.Containers) > 0 && t.RPMOSTree && (t.Name() != "edge-commit" && t.Name() != "edge-container") { + return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.Name(), t.Arch().Distro().Name()) + } + + if options.OSTree != nil { + if err := options.OSTree.Validate(); err != nil { + return warnings, err + } + } + + if t.BootISO && t.RPMOSTree { + // ostree-based ISOs require a URL from which to pull a payload commit + if options.OSTree == nil || options.OSTree.URL == "" { + return warnings, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.Name()) + } + + if t.Name() == "edge-simplified-installer" { + allowed := []string{"InstallationDevice", "FDO", "User", "Group", "FIPS"} + if err := customizations.CheckAllowed(allowed...); err != nil { + return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) + } + if customizations.GetInstallationDevice() == "" { + return warnings, fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.Name()) + } + //making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint + if customizations.GetFDO() != nil { + if customizations.GetFDO().ManufacturingServerURL == "" { + return warnings, fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.Name()) + } + var diunSet int + if customizations.GetFDO().DiunPubKeyHash != "" { + diunSet++ + } + if customizations.GetFDO().DiunPubKeyInsecure != "" { + diunSet++ + } + if customizations.GetFDO().DiunPubKeyRootCerts != "" { + diunSet++ + } + if diunSet != 1 { + return warnings, fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.Name()) + } + } + } else if t.Name() == "edge-installer" { + allowed := []string{"User", "Group", "FIPS", "Installer", "Timezone", "Locale"} + if err := customizations.CheckAllowed(allowed...); err != nil { + return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) + } + } + } + + if t.Name() == "edge-raw-image" { + // ostree-based bootable images require a URL from which to pull a payload commit + if options.OSTree == nil || options.OSTree.URL == "" { + return warnings, fmt.Errorf("%q images require specifying a URL from which to retrieve the OSTree commit", t.Name()) + } + + allowed := []string{"User", "Group", "FIPS"} + if err := customizations.CheckAllowed(allowed...); err != nil { + return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) + } + // TODO: consider additional checks, such as those in "edge-simplified-installer" + } + + if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.RPMOSTree && t.Name() != "edge-raw-image" && t.Name() != "edge-simplified-installer" { + return warnings, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") + } + + mountpoints := customizations.GetFilesystems() + partitioning, err := customizations.GetPartitioning() + if err != nil { + return nil, err + } + + if partitioning != nil { + for _, partition := range partitioning.Partitions { + if t.Arch().Name() == arch.ARCH_AARCH64.String() { + if partition.FSType == "swap" { + return warnings, fmt.Errorf("swap partition creation is not supported on %s %s", t.Arch().Distro().Name(), t.Arch().Name()) + } + for _, lv := range partition.LogicalVolumes { + if lv.FSType == "swap" { + return warnings, fmt.Errorf("swap partition creation is not supported on %s %s", t.Arch().Distro().Name(), t.Arch().Name()) + } + } + } + } + } + + if mountpoints != nil && t.RPMOSTree { + return warnings, fmt.Errorf("Custom mountpoints and partitioning are not supported for ostree types") + } + + if err := blueprint.CheckMountpointsPolicy(mountpoints, policies.MountpointPolicies); err != nil { + return warnings, err + } + + if err := partitioning.ValidateLayoutConstraints(); err != nil { + return warnings, err + } + + if err := blueprint.CheckDiskMountpointsPolicy(partitioning, policies.MountpointPolicies); err != nil { + return warnings, err + } + + if osc := customizations.GetOpenSCAP(); osc != nil { + if t.Arch().Distro().OsVersion() == "9.0" { + return warnings, fmt.Errorf("OpenSCAP unsupported os version: %s", t.Arch().Distro().OsVersion()) + } + if !oscap.IsProfileAllowed(osc.ProfileID, t.arch.distro.DistroYAML.OscapProfilesAllowList) { + return warnings, fmt.Errorf("OpenSCAP unsupported profile: %s", osc.ProfileID) + } + if t.RPMOSTree { + return warnings, fmt.Errorf("OpenSCAP customizations are not supported for ostree types") + } + if osc.ProfileID == "" { + return warnings, fmt.Errorf("OpenSCAP profile cannot be empty") + } + } + + // Check Directory/File Customizations are valid + dc := customizations.GetDirectories() + fc := customizations.GetFiles() + + err = blueprint.ValidateDirFileCustomizations(dc, fc) + if err != nil { + return warnings, err + } + + dcp := policies.CustomDirectoriesPolicies + fcp := policies.CustomFilesPolicies + + if t.RPMOSTree { + dcp = policies.OstreeCustomDirectoriesPolicies + fcp = policies.OstreeCustomFilesPolicies + } + + err = blueprint.CheckDirectoryCustomizationsPolicy(dc, dcp) + if err != nil { + return warnings, err + } + + err = blueprint.CheckFileCustomizationsPolicy(fc, fcp) + if err != nil { + return warnings, err + } + + // check if repository customizations are valid + _, err = customizations.GetRepositories() + if err != nil { + return warnings, err + } + + if customizations.GetFIPS() && !common.IsBuildHostFIPSEnabled() { + w := fmt.Sprintln(common.FIPSEnabledImageWarning) + warnings = append(warnings, w) + } + + instCust, err := customizations.GetInstaller() + if err != nil { + return warnings, err + } + if instCust != nil { + // only supported by the Anaconda installer + if slices.Index([]string{"image-installer", "edge-installer", "live-installer"}, t.Name()) == -1 { + return warnings, fmt.Errorf("installer customizations are not supported for %q", t.Name()) + } + + if t.Name() == "edge-installer" && + instCust.Kickstart != nil && + len(instCust.Kickstart.Contents) > 0 && + (customizations.GetUsers() != nil || customizations.GetGroups() != nil) { + return warnings, fmt.Errorf("edge-installer installer.kickstart.contents are not supported in combination with users or groups") + } + } + + return warnings, nil + +} + func checkOptionsRhel7(t *imageType, bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) { customizations := bp.Customizations // holds warnings (e.g. deprecation notices) diff --git a/pkg/distrofactory/distrofactory.go b/pkg/distrofactory/distrofactory.go index cd362e841b..dd8f768a42 100644 --- a/pkg/distrofactory/distrofactory.go +++ b/pkg/distrofactory/distrofactory.go @@ -6,7 +6,6 @@ import ( "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/distro/generic" - "github.com/osbuild/images/pkg/distro/rhel/rhel8" "github.com/osbuild/images/pkg/distro/rhel/rhel9" "github.com/osbuild/images/pkg/distro/test_distro" ) @@ -108,7 +107,6 @@ func New(factories ...FactoryFunc) *Factory { func NewDefault() *Factory { return New( generic.DistroFactory, - rhel8.DistroFactory, rhel9.DistroFactory, ) } From 08ace268b7c294551a29a0df20ef9749149f2ed9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 13:27:03 +0200 Subject: [PATCH 06/14] distro: add centos-8 to distros.yaml --- pkg/distro/defs/distros.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 4e99aa6641..39eafb0c33 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -182,7 +182,7 @@ distros: - "vhd" runner: name: "org.osbuild.rhel{{.MajorVersion}}{{.MinorVersion}}" - build_packages: &rhel_runner_build_packages + build_packages: &rhel8_runner_build_packages - "glibc" # ldconfig - "systemd" # systemd-tmpfiles and systemd-sysusers - "platform-python" # osbuild @@ -194,6 +194,20 @@ distros: - "python36" oscap_profiles_allowlist: *oscap_profile_allowlist_rhel + - ¢os8 + <<: *rhel8 + name: centos-8 + distro_like: rhel-8 + product: "CentOS Stream" + os_version: "8-stream" + vendor: "centos" + default_fs_type: "xfs" + iso_label_tmpl: "CentOS-Stream-{{.Distro.MajorVersion}}-BaseOS-{{.Arch}}" + runner: + name: org.osbuild.centos8 + build_packages: *rhel8_runner_build_packages + oscap_profiles_allowlist: *oscap_profile_allowlist_rhel + - &rhel7 name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" match: "rhel-7.[0-9]{,[0-9]}" From bb8423153bd6ce4930d831cc4fa3eb5ba035c737 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 14:59:25 +0200 Subject: [PATCH 07/14] distro: add new `transform_re` to the distro This commit adds a new `transform_re` expression to the distro yaml so that a regexp can be used to translate a name into the canonical "name-major.minor". This is needed for rhel-8 where there is support to create a distro with `rhel-810` instead of `rhel-8.10`. This commit also includes the regexp for rhel8 --- pkg/distro/defs/distros.yaml | 2 ++ pkg/distro/defs/loader.go | 30 +++++++++++++++++------- pkg/distro/defs/loader_test.go | 39 +++++++++++++++++++++++++++++++ pkg/distro/generic/fedora_test.go | 4 ---- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 39eafb0c33..9af3e12fc7 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -151,6 +151,8 @@ distros: - &rhel8 name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" match: "rhel-8.[0-9]{,[0-9]}" + # rhel8 support being named "rhel-81" for "rhel-8.1" or "rhel-810" for "rhel-8.10" etc + transform_re: "(?Prhel)-(?P8)(?P[0-9]+)" distro_like: rhel-8 product: "Red Hat Enterprise Linux" os_version: "8.{{.MinorVersion}}" diff --git a/pkg/distro/defs/loader.go b/pkg/distro/defs/loader.go index 1e129def88..b665503fea 100644 --- a/pkg/distro/defs/loader.go +++ b/pkg/distro/defs/loader.go @@ -9,6 +9,7 @@ import ( "io/fs" "os" "path/filepath" + "regexp" "slices" "sort" "text/template" @@ -60,14 +61,16 @@ type distrosYAML struct { type DistroYAML struct { // Match can be used to match multiple versions via a - // fnmatch/glob style expression. We could also use a - // regex and do something like: - // rhel-(?P[0-9]+)\.(?P[0-9]+) - // if we need to be more precise in the future, but for - // now every match will be split into "$distroname-$major.$minor" - // (with minor being optional) + // fnmatch/glob style expression. Match string `yaml:"match"` + // TransformRE can be used to transform a given name + // into the canonical -{,.} form. + // E.g. + // (?Prhel)-(?P8)(?P[0-9]+) + // will support a format like e.g. rhel-810 + TransformRE string `yaml:"transform_re"` + // The distro metadata, can contain go text template strings // for {{.Major}}, {{.Minor}} which will be expanded by the // upper layers. @@ -195,12 +198,23 @@ func NewDistroYAML(nameVer string) (*DistroYAML, error) { break } + // apply any transformations things + transformedNameVer := nameVer + re, err := regexp.Compile(distro.TransformRE) + if err != nil { + return nil, err + } + if l := re.FindStringSubmatch(nameVer); len(l) == 4 { + transformedNameVer = fmt.Sprintf("%s-%s.%s", l[re.SubexpIndex("name")], l[re.SubexpIndex("major")], l[re.SubexpIndex("minor")]) + } + pat, err := glob.Compile(distro.Match) if err != nil { return nil, err } - if pat.Match(nameVer) { - if err := distro.runTemplates(nameVer); err != nil { + + if pat.Match(transformedNameVer) { + if err := distro.runTemplates(transformedNameVer); err != nil { return nil, err } diff --git a/pkg/distro/defs/loader_test.go b/pkg/distro/defs/loader_test.go index 2dc6802c02..d11127f8d0 100644 --- a/pkg/distro/defs/loader_test.go +++ b/pkg/distro/defs/loader_test.go @@ -1199,3 +1199,42 @@ distros: _, err = imgType.PlatformsFor("test-distro-1") assert.EqualError(t, err, `platform conditionals for image type "server-qcow2" should match only once but matched 2 times`) } + +func TestDistrosLoadingTransformRE(t *testing.T) { + fakeDistrosYAML := ` +distros: + - name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" + match: "rhel-8.*" + transform_re: "(?Prhel)-(?P8)(?P[0-9]+)" + os_version: "{{.MajorVersion}}.{{.MinorVersion}}" + release_version: "{{.MajorVersion}}" + module_platform_id: "platform:el{{.MajorVersion}}" +` + baseDir := makeFakeDistrosYAML(t, fakeDistrosYAML, "") + restore := defs.MockDataFS(baseDir) + defer restore() + + for _, tc := range []struct { + nameVer string + expectedDistroNameVer string + expectedOsVersion string + }{ + {"rhel-8.1", "rhel-8.1", "8.1"}, + {"rhel-81", "rhel-8.1", "8.1"}, + {"rhel-8.9", "rhel-8.9", "8.9"}, + {"rhel-89", "rhel-8.9", "8.9"}, + {"rhel-8.10", "rhel-8.10", "8.10"}, + {"rhel-810", "rhel-8.10", "8.10"}, + } { + distro, err := defs.NewDistroYAML(tc.nameVer) + require.NoError(t, err) + assert.Equal(t, &defs.DistroYAML{ + Name: tc.expectedDistroNameVer, + Match: "rhel-8.*", + TransformRE: "(?Prhel)-(?P8)(?P[0-9]+)", + OsVersion: tc.expectedOsVersion, + ReleaseVersion: "8", + ModulePlatformID: "platform:el8", + }, distro) + } +} diff --git a/pkg/distro/generic/fedora_test.go b/pkg/distro/generic/fedora_test.go index dd4cd090b9..fafd1e158c 100644 --- a/pkg/distro/generic/fedora_test.go +++ b/pkg/distro/generic/fedora_test.go @@ -968,10 +968,6 @@ func TestFedoraDistroFactory(t *testing.T) { strID: "rhel-9", expected: nil, }, - { - strID: "rhel-810", - expected: nil, - }, { strID: "rhel-8.4.1", expected: nil, From 7df8e1759e236d96ea46d521c9ed08fceaf65137 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 12:45:41 +0200 Subject: [PATCH 08/14] distro: port rhel8 tests into the generic distro tests Move the rhel8 specific tests into the generic distro tests so that we don't lose test coverage. --- pkg/distro/generic/rhel7_test.go | 34 +- pkg/distro/generic/rhel8_internal_test.go | 153 ++++ pkg/distro/generic/rhel8_test.go | 983 ++++++++++++++++++++++ pkg/distro/rhel/distro_test.go | 2 +- 4 files changed, 1154 insertions(+), 18 deletions(-) create mode 100644 pkg/distro/generic/rhel8_internal_test.go create mode 100644 pkg/distro/generic/rhel8_test.go diff --git a/pkg/distro/generic/rhel7_test.go b/pkg/distro/generic/rhel7_test.go index ea51d07773..287610727f 100644 --- a/pkg/distro/generic/rhel7_test.go +++ b/pkg/distro/generic/rhel7_test.go @@ -17,7 +17,7 @@ type rhelFamilyDistro struct { distro distro.Distro } -var rhelFamilyDistros = []rhelFamilyDistro{ +var rhel7_FamilyDistros = []rhelFamilyDistro{ { name: "rhel-79", distro: generic.DistroFactory("rhel-7.9"), @@ -68,7 +68,7 @@ func TestRhel7FilenameFromType(t *testing.T) { want: wantResult{wantErr: true}, }, } - for _, dist := range rhelFamilyDistros { + for _, dist := range rhel7_FamilyDistros { t.Run(dist.name, func(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -114,7 +114,7 @@ func TestRhel7ImageType_BuildPackages(t *testing.T) { buildPackages := map[string][]string{ "x86_64": x8664BuildPackages, } - for _, dist := range rhelFamilyDistros { + for _, dist := range rhel7_FamilyDistros { t.Run(dist.name, func(t *testing.T) { d := dist.distro for _, archLabel := range d.ListArches() { @@ -154,7 +154,7 @@ func TestRhel7ImageType_Name(t *testing.T) { }, } - for _, dist := range rhelFamilyDistros { + for _, dist := range rhel7_FamilyDistros { t.Run(dist.name, func(t *testing.T) { for _, mapping := range imgMap { arch, err := dist.distro.GetArch(mapping.arch) @@ -174,7 +174,7 @@ func TestRhel7ImageType_Name(t *testing.T) { // Check that Manifest() function returns an error for unsupported // configurations. func TestRhel7Distro_ManifestError(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Kernel: &blueprint.KernelCustomization{ @@ -212,7 +212,7 @@ func TestRhel7Architecture_ListImageTypes(t *testing.T) { }, } - for _, dist := range rhelFamilyDistros { + for _, dist := range rhel7_FamilyDistros { t.Run(dist.name, func(t *testing.T) { for _, mapping := range imgMap { arch, err := dist.distro.GetArch(mapping.arch) @@ -232,7 +232,7 @@ func TestRhel7Architecture_ListImageTypes(t *testing.T) { } func TestRhel7Rhel7_ListArches(t *testing.T) { - arches := rhelFamilyDistros[0].distro.ListArches() + arches := rhel7_FamilyDistros[0].distro.ListArches() assert.Equal(t, []string{"x86_64"}, arches) } @@ -251,7 +251,7 @@ func TestRhel7Rhel7_GetArch(t *testing.T) { }, } - for _, dist := range rhelFamilyDistros { + for _, dist := range rhel7_FamilyDistros { t.Run(dist.name, func(t *testing.T) { for _, a := range arches { actualArch, err := dist.distro.GetArch(a.name) @@ -268,21 +268,21 @@ func TestRhel7Rhel7_GetArch(t *testing.T) { } func TestRhel7Rhel7_Name(t *testing.T) { - distro := rhelFamilyDistros[0].distro + distro := rhel7_FamilyDistros[0].distro assert.Equal(t, "rhel-7.9", distro.Name()) } func TestRhel7Rhel7_ModulePlatformID(t *testing.T) { - distro := rhelFamilyDistros[0].distro + distro := rhel7_FamilyDistros[0].distro assert.Equal(t, "platform:el7", distro.ModulePlatformID()) } func TestRhel7Rhel7_KernelOption(t *testing.T) { - distro_test_common.TestDistro_KernelOption(t, rhelFamilyDistros[0].distro) + distro_test_common.TestDistro_KernelOption(t, rhel7_FamilyDistros[0].distro) } func TestRhel7Distro_CustomFileSystemManifestError(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ @@ -304,7 +304,7 @@ func TestRhel7Distro_CustomFileSystemManifestError(t *testing.T) { } func TestRhel7Distro_TestRhel7RootMountPoint(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ @@ -326,7 +326,7 @@ func TestRhel7Distro_TestRhel7RootMountPoint(t *testing.T) { } func TestRhel7Distro_CustomFileSystemSubDirectories(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ @@ -352,7 +352,7 @@ func TestRhel7Distro_CustomFileSystemSubDirectories(t *testing.T) { } func TestRhel7Distro_MountpointsWithArbitraryDepthAllowed(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ @@ -386,7 +386,7 @@ func TestRhel7Distro_MountpointsWithArbitraryDepthAllowed(t *testing.T) { } func TestRhel7Distro_DirtyMountpointsNotAllowed(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ @@ -416,7 +416,7 @@ func TestRhel7Distro_DirtyMountpointsNotAllowed(t *testing.T) { } func TestRhel7Distro_CustomUsrPartitionNotLargeEnough(t *testing.T) { - r7distro := rhelFamilyDistros[0].distro + r7distro := rhel7_FamilyDistros[0].distro bp := blueprint.Blueprint{ Customizations: &blueprint.Customizations{ Filesystem: []blueprint.FilesystemCustomization{ diff --git a/pkg/distro/generic/rhel8_internal_test.go b/pkg/distro/generic/rhel8_internal_test.go new file mode 100644 index 0000000000..0cf76689cc --- /dev/null +++ b/pkg/distro/generic/rhel8_internal_test.go @@ -0,0 +1,153 @@ +package generic + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/osbuild/images/internal/common" + "github.com/osbuild/images/pkg/blueprint" + "github.com/osbuild/images/pkg/datasizes" + "github.com/osbuild/images/pkg/disk" + "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/distro_test_common" +) + +func TestRH8_EC2Partitioning(t *testing.T) { + testCases := []struct { + distro string + aarch64bootSizeMiB uint64 + }{ + { + distro: "rhel-8.8", + aarch64bootSizeMiB: 512, + }, + { + distro: "rhel-8.9", + aarch64bootSizeMiB: 512, + }, + { + distro: "rhel-8.10", + aarch64bootSizeMiB: 1024, + }, + { + distro: "centos-8", + aarch64bootSizeMiB: 1024, + }, + } + + for _, tt := range testCases { + for _, arch := range []string{"x86_64", "aarch64"} { + for _, it := range []string{"ami", "ec2", "ec2-ha", "ec2-sap"} { + // skip non-existing combos + if strings.HasPrefix(it, "ec2") && strings.HasPrefix(tt.distro, "centos") { + continue + } + if arch == "aarch64" && (it == "ec2-ha" || it == "ec2-sap") { + continue + } + t.Run(fmt.Sprintf("%s/%s/%s", tt.distro, arch, it), func(t *testing.T) { + d := DistroFactory(tt.distro) + require.NotNil(t, d) + a, err := d.GetArch(arch) + require.NoError(t, err) + i, err := a.GetImageType(it) + require.NoError(t, err) + + it := i.(*imageType) + pt, err := it.getPartitionTable(&blueprint.Customizations{}, distro.ImageOptions{}, rng) + require.NoError(t, err) + + // x86_64 is /boot-less, check that + if arch == "x86_64" { + require.Nil(t, err, pt.FindMountable("/boot")) + return + } + + bootSize, err := pt.GetMountpointSize("/boot") + require.NoError(t, err) + require.Equal(t, tt.aarch64bootSizeMiB*datasizes.MiB, bootSize) + }) + + } + } + + } +} + +func TestRH8_DistroFactory(t *testing.T) { + type testCase struct { + strID string + expected distro.Distro + } + + testCases := []testCase{ + { + strID: "rhel-8.0", + expected: common.Must(newDistro("rhel-8.0")), + }, + { + strID: "rhel-80", + expected: common.Must(newDistro("rhel-8.0")), + }, + { + strID: "rhel-8.4", + expected: common.Must(newDistro("rhel-8.4")), + }, + { + strID: "rhel-84", + expected: common.Must(newDistro("rhel-8.4")), + }, + { + strID: "rhel-8.10", + expected: common.Must(newDistro("rhel-8.10")), + }, + { + strID: "rhel-810", + expected: common.Must(newDistro("rhel-8.10")), + }, + { + strID: "centos-8", + expected: common.Must(newDistro("centos-8")), + }, + { + strID: "centos-8.4", + expected: nil, + }, + { + strID: "rhel-8", + expected: nil, + }, + { + strID: "rhel-8.4.1", + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.strID, func(t *testing.T) { + d := DistroFactory(tc.strID) + if tc.expected == nil { + assert.Nil(t, d) + } else { + require.NotNil(t, d) + assert.Equal(t, tc.expected.Name(), d.Name()) + } + }) + } +} + +func RH8_TestESP(t *testing.T) { + var distros []distro.Distro + for _, distroName := range []string{"rhel-8.8", "rhel-8.9", "rhel-8.10", "centos-8"} { + distros = append(distros, DistroFactory(distroName)) + } + + distro_test_common.TestESP(t, distros, func(i distro.ImageType) (*disk.PartitionTable, error) { + it := i.(*imageType) + return it.getPartitionTable(&blueprint.Customizations{}, distro.ImageOptions{}, rng) + }) +} diff --git a/pkg/distro/generic/rhel8_test.go b/pkg/distro/generic/rhel8_test.go new file mode 100644 index 0000000000..5a0c6a91f8 --- /dev/null +++ b/pkg/distro/generic/rhel8_test.go @@ -0,0 +1,983 @@ +package generic_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/osbuild/images/pkg/arch" + "github.com/osbuild/images/pkg/blueprint" + "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/distro_test_common" + "github.com/osbuild/images/pkg/distro/generic" +) + +var rhel8_FamilyDistros = []rhelFamilyDistro{ + { + name: "rhel-810", + distro: generic.DistroFactory("rhel-810"), + }, +} + +func TestRH8_FilenameFromType(t *testing.T) { + type args struct { + outputFormat string + } + type wantResult struct { + filename string + mimeType string + wantErr bool + } + tests := []struct { + name string + args args + want wantResult + }{ + { + name: "ami", + args: args{"ami"}, + want: wantResult{ + filename: "image.raw", + mimeType: "application/octet-stream", + }, + }, + { + name: "ec2", + args: args{"ec2"}, + want: wantResult{ + filename: "image.raw.xz", + mimeType: "application/xz", + }, + }, + { + name: "ec2-ha", + args: args{"ec2-ha"}, + want: wantResult{ + filename: "image.raw.xz", + mimeType: "application/xz", + }, + }, + { + name: "ec2-sap", + args: args{"ec2-sap"}, + want: wantResult{ + filename: "image.raw.xz", + mimeType: "application/xz", + }, + }, + { + name: "qcow2", + args: args{"qcow2"}, + want: wantResult{ + filename: "disk.qcow2", + mimeType: "application/x-qemu-disk", + }, + }, + { + name: "openstack", + args: args{"openstack"}, + want: wantResult{ + filename: "disk.qcow2", + mimeType: "application/x-qemu-disk", + }, + }, + { + name: "vhd", + args: args{"vhd"}, + want: wantResult{ + filename: "disk.vhd", + mimeType: "application/x-vhd", + }, + }, + { + name: "azure-rhui", + args: args{"azure-rhui"}, + want: wantResult{ + filename: "disk.vhd.xz", + mimeType: "application/xz", + }, + }, + { + name: "azure-sap-rhui", + args: args{"azure-sap-rhui"}, + want: wantResult{ + filename: "disk.vhd.xz", + mimeType: "application/xz", + }, + }, + { + name: "vmdk", + args: args{"vmdk"}, + want: wantResult{ + filename: "disk.vmdk", + mimeType: "application/x-vmdk", + }, + }, + { + name: "ova", + args: args{"ova"}, + want: wantResult{ + filename: "image.ova", + mimeType: "application/ovf", + }, + }, + { + name: "tar", + args: args{"tar"}, + want: wantResult{ + filename: "root.tar.xz", + mimeType: "application/x-tar", + }, + }, + { + name: "image-installer", + args: args{"image-installer"}, + want: wantResult{ + filename: "installer.iso", + mimeType: "application/x-iso9660-image", + }, + }, + { + name: "edge-commit", + args: args{"edge-commit"}, + want: wantResult{ + filename: "commit.tar", + mimeType: "application/x-tar", + }, + }, + // Alias + { + name: "rhel-edge-commit", + args: args{"rhel-edge-commit"}, + want: wantResult{ + filename: "commit.tar", + mimeType: "application/x-tar", + }, + }, + { + name: "edge-container", + args: args{"edge-container"}, + want: wantResult{ + filename: "container.tar", + mimeType: "application/x-tar", + }, + }, + // Alias + { + name: "rhel-edge-container", + args: args{"rhel-edge-container"}, + want: wantResult{ + filename: "container.tar", + mimeType: "application/x-tar", + }, + }, + { + name: "edge-installer", + args: args{"edge-installer"}, + want: wantResult{ + filename: "installer.iso", + mimeType: "application/x-iso9660-image", + }, + }, + // Alias + { + name: "rhel-edge-installer", + args: args{"rhel-edge-installer"}, + want: wantResult{ + filename: "installer.iso", + mimeType: "application/x-iso9660-image", + }, + }, + { + name: "gce", + args: args{"gce"}, + want: wantResult{ + filename: "image.tar.gz", + mimeType: "application/gzip", + }, + }, + { + name: "gce-rhui", + args: args{"gce-rhui"}, + want: wantResult{ + filename: "image.tar.gz", + mimeType: "application/gzip", + }, + }, + { + name: "invalid-output-type", + args: args{"foobar"}, + want: wantResult{wantErr: true}, + }, + { + name: "minimal-raw", + args: args{"minimal-raw"}, + want: wantResult{ + filename: "disk.raw.xz", + mimeType: "application/xz", + }, + }, + } + for _, dist := range rhel8_FamilyDistros { + t.Run(dist.name, func(t *testing.T) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dist := dist.distro + arch, _ := dist.GetArch("x86_64") + imgType, err := arch.GetImageType(tt.args.outputFormat) + if tt.want.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotNil(t, imgType) + gotFilename := imgType.Filename() + gotMIMEType := imgType.MIMEType() + if gotFilename != tt.want.filename { + t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename) + } + if gotMIMEType != tt.want.mimeType { + t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType) + } + } + }) + } + }) + } +} + +func TestRH8_ImageType_BuildPackages(t *testing.T) { + x8664BuildPackages := []string{ + "dnf", + "dosfstools", + "e2fsprogs", + "grub2-efi-x64", + "grub2-pc", + "policycoreutils", + "shim-x64", + "systemd", + "tar", + "qemu-img", + "xz", + } + aarch64BuildPackages := []string{ + "dnf", + "dosfstools", + "e2fsprogs", + "policycoreutils", + "qemu-img", + "systemd", + "tar", + "xz", + } + buildPackages := map[string][]string{ + "x86_64": x8664BuildPackages, + "aarch64": aarch64BuildPackages, + } + for _, dist := range rhel8_FamilyDistros { + t.Run(dist.name, func(t *testing.T) { + d := dist.distro + for _, archLabel := range d.ListArches() { + archStruct, err := d.GetArch(archLabel) + if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) { + continue + } + for _, itLabel := range archStruct.ListImageTypes() { + itStruct, err := archStruct.GetImageType(itLabel) + if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) { + continue + } + manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, nil) + assert.NoError(t, err) + buildPkgs := manifest.GetPackageSetChains()["build"] + assert.NotNil(t, buildPkgs) + assert.Len(t, buildPkgs, 1) + assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include) + } + } + }) + } +} + +func TestRH8_ImageType_Name(t *testing.T) { + imgMap := []struct { + arch string + imgNames []string + }{ + { + arch: "x86_64", + imgNames: []string{ + "qcow2", + "openstack", + "vhd", + "azure-rhui", + "azure-sap-rhui", + "azure-eap7-rhui", + "vmdk", + "ova", + "ami", + "ec2", + "ec2-ha", + "ec2-sap", + "gce", + "gce-rhui", + "edge-commit", + "edge-container", + "edge-installer", + "tar", + "image-installer", + "minimal-raw", + }, + }, + { + arch: "aarch64", + imgNames: []string{ + "qcow2", + "openstack", + "vhd", + "azure-rhui", + "ami", + "ec2", + "edge-commit", + "edge-container", + "tar", + "minimal-raw", + }, + }, + { + arch: "ppc64le", + imgNames: []string{ + "qcow2", + "tar", + }, + }, + { + arch: "s390x", + imgNames: []string{ + "qcow2", + "tar", + }, + }, + } + + for _, dist := range rhel8_FamilyDistros { + t.Run(dist.name, func(t *testing.T) { + for _, mapping := range imgMap { + if mapping.arch == arch.ARCH_S390X.String() && dist.name == "centos" { + continue + } + arch, err := dist.distro.GetArch(mapping.arch) + if assert.NoError(t, err) { + for _, imgName := range mapping.imgNames { + if imgName == "edge-commit" && dist.name == "centos" { + continue + } + imgType, err := arch.GetImageType(imgName) + if assert.NoError(t, err) { + assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch) + } + } + } + } + }) + } +} + +func TestRH8_ImageTypeAliases(t *testing.T) { + type args struct { + imageTypeAliases []string + } + type wantResult struct { + imageTypeName string + } + tests := []struct { + name string + args args + want wantResult + }{ + { + name: "edge-commit aliases", + args: args{ + imageTypeAliases: []string{"rhel-edge-commit"}, + }, + want: wantResult{ + imageTypeName: "edge-commit", + }, + }, + { + name: "edge-container aliases", + args: args{ + imageTypeAliases: []string{"rhel-edge-container"}, + }, + want: wantResult{ + imageTypeName: "edge-container", + }, + }, + { + name: "edge-installer aliases", + args: args{ + imageTypeAliases: []string{"rhel-edge-installer"}, + }, + want: wantResult{ + imageTypeName: "edge-installer", + }, + }, + } + for _, dist := range rhel8_FamilyDistros { + t.Run(dist.name, func(t *testing.T) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dist := dist.distro + for _, archName := range dist.ListArches() { + t.Run(archName, func(t *testing.T) { + arch, err := dist.GetArch(archName) + require.Nilf(t, err, + "failed to get architecture '%s', previously listed as supported for the distro '%s'", + archName, dist.Name()) + // Test image type aliases only if the aliased image type is supported for the arch + if _, err = arch.GetImageType(tt.want.imageTypeName); err != nil { + t.Skipf("aliased image type '%s' is not supported for architecture '%s'", + tt.want.imageTypeName, archName) + } + for _, alias := range tt.args.imageTypeAliases { + t.Run(fmt.Sprintf("'%s' alias for image type '%s'", alias, tt.want.imageTypeName), + func(t *testing.T) { + gotImage, err := arch.GetImageType(alias) + require.Nilf(t, err, "arch.GetImageType() for image type alias '%s' failed: %v", + alias, err) + assert.Equalf(t, tt.want.imageTypeName, gotImage.Name(), + "got unexpected image type name for alias '%s'. got = %s, want = %s", + alias, tt.want.imageTypeName, gotImage.Name()) + }) + } + }) + } + }) + } + }) + } +} + +// Check that Manifest() function returns an error for unsupported +// configurations. +func TestRH8_Distro_ManifestError(t *testing.T) { + // Currently, the only unsupported configuration is OSTree commit types + // with Kernel boot options + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Kernel: &blueprint.KernelCustomization{ + Append: "debug", + }, + }, + } + + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + imgOpts := distro.ImageOptions{ + Size: imgType.Size(0), + } + _, _, err := imgType.Manifest(&bp, imgOpts, nil, nil) + if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { + assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types") + } else if imgTypeName == "edge-raw-image" { + assert.EqualError(t, err, fmt.Sprintf("%q images require specifying a URL from which to retrieve the OSTree commit", imgTypeName)) + } else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" { + assert.EqualError(t, err, fmt.Sprintf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", imgTypeName)) + } else { + assert.NoError(t, err) + } + } + } +} + +func TestRH8_Architecture_ListImageTypes(t *testing.T) { + imgMap := []struct { + arch string + imgNames []string + rhelAdditionalImageTypes []string + }{ + { + arch: "x86_64", + imgNames: []string{ + "qcow2", + "openstack", + "vhd", + "azure-rhui", + "azure-sap-rhui", + "azure-eap7-rhui", + "vmdk", + "ova", + "ami", + "ec2", + "ec2-ha", + "ec2-sap", + "gce", + "gce-rhui", + "edge-commit", + "edge-container", + "edge-installer", + "edge-raw-image", + "edge-simplified-installer", + "tar", + "image-installer", + "oci", + "wsl", + "minimal-raw", + }, + }, + { + arch: "aarch64", + imgNames: []string{ + "qcow2", + "openstack", + "vhd", + "azure-rhui", + "ami", + "ec2", + "edge-commit", + "edge-container", + "edge-installer", + "edge-simplified-installer", + "edge-raw-image", + "tar", + "image-installer", + "wsl", + "minimal-raw", + }, + }, + { + arch: "ppc64le", + imgNames: []string{ + "qcow2", + "tar", + }, + }, + { + arch: "s390x", + imgNames: []string{ + "qcow2", + "tar", + }, + }, + } + + for _, dist := range rhel8_FamilyDistros { + for _, mapping := range imgMap { + t.Run(dist.name+"/"+mapping.arch, func(t *testing.T) { + + arch, err := dist.distro.GetArch(mapping.arch) + require.NoError(t, err) + imageTypes := arch.ListImageTypes() + + var expectedImageTypes []string + expectedImageTypes = append(expectedImageTypes, mapping.imgNames...) + if dist.name == "rhel" { + expectedImageTypes = append(expectedImageTypes, mapping.rhelAdditionalImageTypes...) + } + + require.ElementsMatch(t, expectedImageTypes, imageTypes) + }) + } + } +} + +func TestRHEL8_ListArches(t *testing.T) { + arches := rhel8_FamilyDistros[0].distro.ListArches() + assert.Equal(t, []string{"aarch64", "ppc64le", "s390x", "x86_64"}, arches) +} + +func TestRHEL8_GetArch(t *testing.T) { + arches := []struct { + name string + errorExpected bool + errorExpectedInCentos bool + }{ + { + name: "x86_64", + }, + { + name: "aarch64", + }, + { + name: "ppc64le", + }, + { + name: "s390x", + }, + { + name: "foo-arch", + errorExpected: true, + }, + } + + for _, dist := range rhel8_FamilyDistros { + t.Run(dist.name, func(t *testing.T) { + for _, a := range arches { + actualArch, err := dist.distro.GetArch(a.name) + if a.errorExpected || (a.errorExpectedInCentos && dist.name == "centos") { + assert.Nil(t, actualArch) + assert.Error(t, err) + } else { + require.Equal(t, a.name, actualArch.Name()) + assert.NoError(t, err) + } + } + }) + } +} + +func TestRhel8_Name(t *testing.T) { + distro := rhel8_FamilyDistros[0].distro + assert.Equal(t, "rhel-8.10", distro.Name()) +} + +func TestRhel8_ModulePlatformID(t *testing.T) { + distro := rhel8_FamilyDistros[0].distro + assert.Equal(t, "platform:el8", distro.ModulePlatformID()) +} + +func TestRhel86_KernelOption(t *testing.T) { + distro_test_common.TestDistro_KernelOption(t, rhel8_FamilyDistros[0].distro) +} + +func TestRhel8_OSTreeOptions(t *testing.T) { + distro_test_common.TestDistro_OSTreeOptions(t, rhel8_FamilyDistros[0].distro) +} + +func TestRH8_Distro_CustomFileSystemManifestError(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "/etc", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { + assert.EqualError(t, err, "Custom mountpoints and partitioning are not supported for ostree types") + } else if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.EqualError(t, err, "The following errors occurred while setting up custom mountpoints:\npath \"/etc\" is not allowed") + } + } + } +} + +func TestRH8_Distro_TestRootMountPoint(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "/", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { + assert.EqualError(t, err, "Custom mountpoints and partitioning are not supported for ostree types") + } else if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } + } +} + +func TestRH8_Distro_CustomFileSystemSubDirectories(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "/var/log", + }, + { + MinSize: 1024, + Mountpoint: "/var/log/audit", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-commit": true, + "edge-container": true, + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } + } +} + +func TestRH8_Distro_MountpointsWithArbitraryDepthAllowed(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "/var/a", + }, + { + MinSize: 1024, + Mountpoint: "/var/a/b", + }, + { + MinSize: 1024, + Mountpoint: "/var/a/b/c", + }, + { + MinSize: 1024, + Mountpoint: "/var/a/b/c/d", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-commit": true, + "edge-container": true, + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } + } +} + +func TestRH8_Distro_DirtyMountpointsNotAllowed(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "//", + }, + { + MinSize: 1024, + Mountpoint: "/var//", + }, + { + MinSize: 1024, + Mountpoint: "/var//log/audit/", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-commit": true, + "edge-container": true, + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + "azure-eap7-rhui": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.EqualError(t, err, "The following errors occurred while setting up custom mountpoints:\npath \"//\" must be canonical\npath \"/var//\" must be canonical\npath \"/var//log/audit/\" must be canonical") + } + } + } +} + +func TestRH8_Distro_CustomUsrPartitionNotLargeEnough(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + bp := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Filesystem: []blueprint.FilesystemCustomization{ + { + MinSize: 1024, + Mountpoint: "/usr", + }, + }, + }, + } + unsupported := map[string]bool{ + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + } + for _, archName := range r8distro.ListArches() { + arch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range arch.ListImageTypes() { + imgType, _ := arch.GetImageType(imgTypeName) + _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) + if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { + assert.EqualError(t, err, "Custom mountpoints and partitioning are not supported for ostree types") + } else if unsupported[imgTypeName] { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } + } +} + +func TestRH8_DiskCustomizationsCheckOptions(t *testing.T) { + r8distro := rhel8_FamilyDistros[0].distro + plainBP := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Disk: &blueprint.DiskCustomization{ + Partitions: []blueprint.PartitionCustomization{ + { + Type: "plain", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/", + Label: "root", + FSType: "ext4", + }, + }, + }, + }, + }, + } + + bpWithSwap := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Disk: &blueprint.DiskCustomization{ + Partitions: []blueprint.PartitionCustomization{ + { + Type: "plain", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/", + Label: "root", + FSType: "ext4", + }, + }, + { + Type: "plain", + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + FSType: "swap", + }, + }, + }, + }, + }, + } + + bpWithSwapLV := blueprint.Blueprint{ + Customizations: &blueprint.Customizations{ + Disk: &blueprint.DiskCustomization{ + Partitions: []blueprint.PartitionCustomization{ + { + Type: "lvm", + VGCustomization: blueprint.VGCustomization{ + LogicalVolumes: []blueprint.LVCustomization{ + { + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + Mountpoint: "/", + Label: "root", + FSType: "ext4", + }, + }, + { + FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ + FSType: "swap", + }, + }, + }, + }, + }, + }, + }, + }, + } + + // these produce a different error message and are tested elsewhere + skipTest := map[string]bool{ + "edge-installer": true, + "edge-simplified-installer": true, + "edge-raw-image": true, + "azure-eap7-rhui": true, + } + + for _, archName := range r8distro.ListArches() { + distroArch, _ := r8distro.GetArch(archName) + for _, imgTypeName := range distroArch.ListImageTypes() { + imgType, _ := distroArch.GetImageType(imgTypeName) + if skipTest[imgTypeName] { + continue + } + + testname := fmt.Sprintf("%s-%s-%s", r8distro.Name(), archName, imgTypeName) + t.Run(testname, func(t *testing.T) { + assert := assert.New(t) + + _, _, err := imgType.Manifest(&plainBP, distro.ImageOptions{}, nil, nil) + assert.NoError(err) + + _, _, err = imgType.Manifest(&bpWithSwap, distro.ImageOptions{}, nil, nil) + if archName == arch.ARCH_AARCH64.String() { + assert.EqualError(err, fmt.Sprintf("swap partition creation is not supported on %s %s", r8distro.Name(), archName)) + } else { + assert.NoError(err) + } + + _, _, err = imgType.Manifest(&bpWithSwapLV, distro.ImageOptions{}, nil, nil) + if archName == arch.ARCH_AARCH64.String() { + assert.EqualError(err, fmt.Sprintf("swap partition creation is not supported on %s %s", r8distro.Name(), archName)) + } else { + assert.NoError(err) + } + }) + } + } +} diff --git a/pkg/distro/rhel/distro_test.go b/pkg/distro/rhel/distro_test.go index dad30a2746..843e744cad 100644 --- a/pkg/distro/rhel/distro_test.go +++ b/pkg/distro/rhel/distro_test.go @@ -23,7 +23,7 @@ var rng = rand.New(rand.NewSource(0)) func TestESP(t *testing.T) { var distros []distro.Distro distroFactory := distrofactory.NewDefault() - for _, distroName := range []string{"rhel-8.8", "rhel-8.9", "rhel-8.10", "centos-8", "rhel-9.0", "rhel-9.2", "rhel-9.4", "centos-9"} { + for _, distroName := range []string{"rhel-9.0", "rhel-9.2", "rhel-9.4", "centos-9"} { distros = append(distros, distroFactory.GetDistro(distroName)) } From fa4ab7a6dba4aa8980d3209511bbbbc8a2c64264 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 16:21:37 +0200 Subject: [PATCH 09/14] distro: add edge-{commit,container} to skipTest for rhel8 This commit adds `edge-{commit,container}` to the skipTest for TestRH8_DiskCustomizationsCheckOptions(). The reason is that the original rhel8 checkOptions() did not check if partitioning customizations were used (unlike e.g. rhel10) so the original rhel8 check was incomplete, with the new stricter check we need to skip these image types just like we do on rhel-10. --- pkg/distro/generic/rhel8_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/distro/generic/rhel8_test.go b/pkg/distro/generic/rhel8_test.go index 5a0c6a91f8..a66ddeddc7 100644 --- a/pkg/distro/generic/rhel8_test.go +++ b/pkg/distro/generic/rhel8_test.go @@ -943,6 +943,8 @@ func TestRH8_DiskCustomizationsCheckOptions(t *testing.T) { // these produce a different error message and are tested elsewhere skipTest := map[string]bool{ + "edge-commit": true, + "edge-container": true, "edge-installer": true, "edge-simplified-installer": true, "edge-raw-image": true, From 5890a0d6a4ba9b9852e0c8c41c44397ac46169d8 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 17:28:25 +0200 Subject: [PATCH 10/14] distro,distroidparser: implement defs.ParseID For the special cases in rhel8,rhel9 we need a version of ParseID that comes from YAML (or it could have come from starlark if this was a PR that would have been actually accepted). So this commit implements `defs.ParseID()` that will apply any `transform_re` expressions that can be used to create special cases for naming of distros. --- pkg/distro/defs/distros.yaml | 2 +- pkg/distro/defs/id.go | 32 ++++++++++++++++++++++ pkg/distro/defs/loader.go | 49 ++++++++++++++++++---------------- pkg/distroidparser/idparser.go | 4 +-- 4 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 pkg/distro/defs/id.go diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 9af3e12fc7..593a6a5bf3 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -152,7 +152,7 @@ distros: name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" match: "rhel-8.[0-9]{,[0-9]}" # rhel8 support being named "rhel-81" for "rhel-8.1" or "rhel-810" for "rhel-8.10" etc - transform_re: "(?Prhel)-(?P8)(?P[0-9]+)" + transform_re: "^(?Prhel)-(?P8)(?P[0-9]{1,2})$" distro_like: rhel-8 product: "Red Hat Enterprise Linux" os_version: "8.{{.MinorVersion}}" diff --git a/pkg/distro/defs/id.go b/pkg/distro/defs/id.go new file mode 100644 index 0000000000..4a56b4f28c --- /dev/null +++ b/pkg/distro/defs/id.go @@ -0,0 +1,32 @@ +package defs + +import ( + "fmt" + "regexp" + + "github.com/osbuild/images/pkg/distro" +) + +// ParseID parse the given nameVer into a distro.ID. It will also +// apply any matching `transform_re`. This is needed to support distro +// names like "rhel-810" without dots. +// +// If no transformations are needed it will return "nil" +func ParseID(nameVer string) (*distro.ID, error) { + distros, err := loadDistros() + if err != nil { + return nil, err + } + + for _, d := range distros.Distros { + re, err := regexp.Compile(d.TransformRE) + if err != nil { + return nil, err + } + if l := re.FindStringSubmatch(nameVer); len(l) == 4 { + transformed := fmt.Sprintf("%s-%s.%s", l[re.SubexpIndex("name")], l[re.SubexpIndex("major")], l[re.SubexpIndex("minor")]) + return distro.ParseID(transformed) + } + } + return nil, nil +} diff --git a/pkg/distro/defs/loader.go b/pkg/distro/defs/loader.go index b665503fea..5db32fe468 100644 --- a/pkg/distro/defs/loader.go +++ b/pkg/distro/defs/loader.go @@ -9,7 +9,6 @@ import ( "io/fs" "os" "path/filepath" - "regexp" "slices" "sort" "text/template" @@ -169,14 +168,7 @@ func (d *DistroYAML) runTemplates(nameVer string) error { return errors.Join(errs...) } -// NewDistroYAML return the given distro or nil if the distro is not -// found. This mimics the "distrofactory.GetDistro() interface. -// -// Note that eventually we want something like "Distros()" instead -// that returns all known distros but for now we keep compatibility -// with the way distrofactory/reporegistry work which is by defining -// distros via repository files. -func NewDistroYAML(nameVer string) (*DistroYAML, error) { +func loadDistros() (*distrosYAML, error) { f, err := dataFS().Open("distros.yaml") if err != nil { return nil, err @@ -191,6 +183,27 @@ func NewDistroYAML(nameVer string) (*DistroYAML, error) { return nil, err } + return &distros, nil +} + +// NewDistroYAML return the given distro or nil if the distro is not +// found. This mimics the "distrofactory.GetDistro() interface. +// +// Note that eventually we want something like "Distros()" instead +// that returns all known distros but for now we keep compatibility +// with the way distrofactory/reporegistry work which is by defining +// distros via repository files. +func NewDistroYAML(nameVer string) (*DistroYAML, error) { + distros, err := loadDistros() + if err != nil { + return nil, err + } + + // ParseID will also canonicalize the name + if id, err := ParseID(nameVer); err == nil && id != nil { + nameVer = id.String() + } + var foundDistro *DistroYAML for _, distro := range distros.Distros { if distro.Name == nameVer { @@ -198,23 +211,13 @@ func NewDistroYAML(nameVer string) (*DistroYAML, error) { break } - // apply any transformations things - transformedNameVer := nameVer - re, err := regexp.Compile(distro.TransformRE) - if err != nil { - return nil, err - } - if l := re.FindStringSubmatch(nameVer); len(l) == 4 { - transformedNameVer = fmt.Sprintf("%s-%s.%s", l[re.SubexpIndex("name")], l[re.SubexpIndex("major")], l[re.SubexpIndex("minor")]) - } - pat, err := glob.Compile(distro.Match) if err != nil { return nil, err } - if pat.Match(transformedNameVer) { - if err := distro.runTemplates(transformedNameVer); err != nil { + if pat.Match(nameVer) { + if err := distro.runTemplates(nameVer); err != nil { return nil, err } @@ -227,14 +230,14 @@ func NewDistroYAML(nameVer string) (*DistroYAML, error) { } // load imageTypes - f, err = dataFS().Open(filepath.Join(foundDistro.DefsPath, "distro.yaml")) + f, err := dataFS().Open(filepath.Join(foundDistro.DefsPath, "distro.yaml")) if err != nil { return nil, err } defer f.Close() var toplevel imageTypesYAML - decoder = yaml.NewDecoder(f) + decoder := yaml.NewDecoder(f) decoder.KnownFields(true) if err := decoder.Decode(&toplevel); err != nil { return nil, err diff --git a/pkg/distroidparser/idparser.go b/pkg/distroidparser/idparser.go index 5afea89176..8b63309877 100644 --- a/pkg/distroidparser/idparser.go +++ b/pkg/distroidparser/idparser.go @@ -2,7 +2,7 @@ package distroidparser import ( "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/rhel/rhel8" + "github.com/osbuild/images/pkg/distro/defs" "github.com/osbuild/images/pkg/distro/rhel/rhel9" ) @@ -60,7 +60,7 @@ func (p *Parser) Standardize(idStr string) (string, error) { func NewDefaultParser() *Parser { return New( - rhel8.ParseID, + defs.ParseID, rhel9.ParseID, ) } From 16f2ecc359d6ed2dfbf3c315c316df555f0ac65c Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 9 Jul 2025 17:33:20 +0200 Subject: [PATCH 11/14] distro: rm -rf rhel8 --- pkg/distro/rhel/rhel8/ami.go | 130 --- pkg/distro/rhel/rhel8/azure.go | 111 -- pkg/distro/rhel/rhel8/bare_metal.go | 48 - pkg/distro/rhel/rhel8/distro.go | 455 -------- pkg/distro/rhel/rhel8/distro_internal_test.go | 182 ---- pkg/distro/rhel/rhel8/distro_test.go | 985 ------------------ pkg/distro/rhel/rhel8/edge.go | 155 --- pkg/distro/rhel/rhel8/gce.go | 49 - pkg/distro/rhel/rhel8/options.go | 207 ---- pkg/distro/rhel/rhel8/package_sets.go | 19 - pkg/distro/rhel/rhel8/partition_tables.go | 23 - pkg/distro/rhel/rhel8/qcow2.go | 66 -- pkg/distro/rhel/rhel8/vmdk.go | 45 - pkg/distro/rhel/rhel8/wsl.go | 23 - 14 files changed, 2498 deletions(-) delete mode 100644 pkg/distro/rhel/rhel8/ami.go delete mode 100644 pkg/distro/rhel/rhel8/azure.go delete mode 100644 pkg/distro/rhel/rhel8/bare_metal.go delete mode 100644 pkg/distro/rhel/rhel8/distro.go delete mode 100644 pkg/distro/rhel/rhel8/distro_internal_test.go delete mode 100644 pkg/distro/rhel/rhel8/distro_test.go delete mode 100644 pkg/distro/rhel/rhel8/edge.go delete mode 100644 pkg/distro/rhel/rhel8/gce.go delete mode 100644 pkg/distro/rhel/rhel8/options.go delete mode 100644 pkg/distro/rhel/rhel8/package_sets.go delete mode 100644 pkg/distro/rhel/rhel8/partition_tables.go delete mode 100644 pkg/distro/rhel/rhel8/qcow2.go delete mode 100644 pkg/distro/rhel/rhel8/vmdk.go delete mode 100644 pkg/distro/rhel/rhel8/wsl.go diff --git a/pkg/distro/rhel/rhel8/ami.go b/pkg/distro/rhel/rhel8/ami.go deleted file mode 100644 index f6503c99e6..0000000000 --- a/pkg/distro/rhel/rhel8/ami.go +++ /dev/null @@ -1,130 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkAmiImgTypeX86_64(d *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ami", - "image.raw", - "application/octet-stream", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image"}, - []string{"image"}, - ) - - it.DefaultImageConfig = imageConfig(d, "x86_64", "ami") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkEc2ImgTypeX86_64(rd *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ec2", - "image.raw.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, "x86_64", "ec2") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkEc2HaImgTypeX86_64(rd *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ec2-ha", - "image.raw.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, "x86_64", "ec2-ha") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkAmiImgTypeAarch64(rd *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ami", - "image.raw", - "application/octet-stream", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image"}, - []string{"image"}, - ) - - it.DefaultImageConfig = imageConfig(rd, "aarch64", "ami") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkEc2ImgTypeAarch64(rd *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ec2", - "image.raw.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, "aarch64", "ec2") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkEc2SapImgTypeX86_64(rd *rhel.Distribution) *rhel.ImageType { - it := rhel.NewImageType( - "ec2-sap", - "image.raw.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, "x86_64", "ec2-sap") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/azure.go b/pkg/distro/rhel/rhel8/azure.go deleted file mode 100644 index 52205ea638..0000000000 --- a/pkg/distro/rhel/rhel8/azure.go +++ /dev/null @@ -1,111 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkAzureRhuiImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "azure-rhui", - "disk.vhd.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vpc", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, a.String(), "azure-rhui") - it.Bootable = true - it.DefaultSize = 64 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkAzureSapRhuiImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "azure-sap-rhui", - "disk.vhd.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vpc", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, a.String(), "azure-sap-rhui") - it.Bootable = true - it.DefaultSize = 64 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkAzureByosImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "vhd", - "disk.vhd", - "application/x-vhd", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vpc"}, - []string{"vpc"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "vhd") - it.Bootable = true - it.DefaultSize = 4 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -// Azure non-RHEL image type -func mkAzureImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "vhd", - "disk.vhd", - "application/x-vhd", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vpc"}, - []string{"vpc"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "vhd") - it.Bootable = true - it.DefaultSize = 4 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkAzureEap7RhuiImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "azure-eap7-rhui", - "disk.vhd.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vpc", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, a.String(), "azure-eap7-rhui") - it.Bootable = true - it.DefaultSize = 64 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/bare_metal.go b/pkg/distro/rhel/rhel8/bare_metal.go deleted file mode 100644 index a3ddd955e9..0000000000 --- a/pkg/distro/rhel/rhel8/bare_metal.go +++ /dev/null @@ -1,48 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkImageInstaller(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "image-installer", - "installer.iso", - "application/x-iso9660-image", - packageSetLoader, - rhel.ImageInstallerImage, - []string{"build"}, - []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "os", "bootiso-tree", "bootiso"}, - []string{"bootiso"}, - ) - - it.BootISO = true - it.Bootable = true - it.ISOLabelFn = distroISOLabelFunc - - it.DefaultImageConfig = imageConfig(rd, a.String(), "image-installer") - it.DefaultInstallerConfig = &distro.InstallerConfig{ - AdditionalDracutModules: []string{ - "ifcfg", - }, - } - - return it -} - -func mkTarImgType() *rhel.ImageType { - it := rhel.NewImageType( - "tar", - "root.tar.xz", - "application/x-tar", - packageSetLoader, - rhel.TarImage, - []string{"build"}, - []string{"os", "archive"}, - []string{"archive"}, - ) - - return it -} diff --git a/pkg/distro/rhel/rhel8/distro.go b/pkg/distro/rhel/rhel8/distro.go deleted file mode 100644 index a20b1b604e..0000000000 --- a/pkg/distro/rhel/rhel8/distro.go +++ /dev/null @@ -1,455 +0,0 @@ -package rhel8 - -import ( - "fmt" - "strings" - - "github.com/osbuild/images/internal/common" - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/customizations/oscap" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/defs" - "github.com/osbuild/images/pkg/distro/rhel" - "github.com/osbuild/images/pkg/platform" -) - -var ( - // rhel8 allow all - oscapProfileAllowList = []oscap.Profile{ - oscap.AnssiBp28Enhanced, - oscap.AnssiBp28High, - oscap.AnssiBp28Intermediary, - oscap.AnssiBp28Minimal, - oscap.Cis, - oscap.CisServerL1, - oscap.CisWorkstationL1, - oscap.CisWorkstationL2, - oscap.Cui, - oscap.E8, - oscap.Hippa, - oscap.IsmO, - oscap.Ospp, - oscap.PciDss, - oscap.Stig, - oscap.StigGui, - } -) - -// RHEL-based OS image configuration defaults -func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { - return common.Must(defs.DistroImageConfig(d.Name())) -} - -func distroISOLabelFunc(t *rhel.ImageType) string { - const RHEL_ISO_LABEL = "RHEL-%s-%s-0-BaseOS-%s" - const CS_ISO_LABEL = "CentOS-Stream-%s-%s-dvd" - const ALMALINUX_ISO_LABEL = "AlmaLinux-%s-%s-%s-dvd" - - if t.IsRHEL() { - osVer := strings.Split(t.Arch().Distro().OsVersion(), ".") - return fmt.Sprintf(RHEL_ISO_LABEL, osVer[0], osVer[1], t.Arch().Name()) - } else if t.IsAlmaLinux() { - osVer := strings.Split(t.Arch().Distro().OsVersion(), ".") - return fmt.Sprintf(ALMALINUX_ISO_LABEL, osVer[0], osVer[1], t.Arch().Name()) - } else { - return fmt.Sprintf(CS_ISO_LABEL, t.Arch().Distro().Releasever(), t.Arch().Name()) - } -} - -func newDistro(name string, minor int) *rhel.Distribution { - rd, err := rhel.NewDistribution(name, 8, minor) - if err != nil { - panic(err) - } - - rd.CheckOptions = checkOptions - rd.DefaultImageConfig = defaultDistroImageConfig - - // Architecture definitions - x86_64 := rhel.NewArchitecture(rd, arch.ARCH_X86_64) - aarch64 := rhel.NewArchitecture(rd, arch.ARCH_AARCH64) - ppc64le := rhel.NewArchitecture(rd, arch.ARCH_PPC64LE) - s390x := rhel.NewArchitecture(rd, arch.ARCH_S390X) - - x86_64.AddImageTypes( - &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - QCOW2Compat: "0.10", - }, - }, - mkQcow2ImgType(rd, arch.ARCH_X86_64), - mkOCIImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - }, - }, - mkOpenstackImgType(rd, arch.ARCH_X86_64), - ) - - ec2X86Platform := &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_RAW, - }, - } - - // Keep the RHEL EC2 x86_64 images before 8.9 BIOS-only for backward compatibility. - // RHEL-internal EC2 images and RHEL AMI images are kept intentionally in sync - // with regard to not supporting hybrid boot mode before RHEL version 8.9. - // The partitioning table for these reflects that and is also intentionally in sync. - if rd.IsRHEL() && common.VersionLessThan(rd.OsVersion(), "8.9") { - ec2X86Platform.UEFIVendor = "" - } - - x86_64.AddImageTypes( - ec2X86Platform, - mkAmiImgTypeX86_64(rd), - ) - - bareMetalX86Platform := &platform.X86{ - BasePlatform: platform.BasePlatform{ - FirmwarePackages: []string{ - "microcode_ctl", // ?? - "iwl1000-firmware", - "iwl100-firmware", - "iwl105-firmware", - "iwl135-firmware", - "iwl2000-firmware", - "iwl2030-firmware", - "iwl3160-firmware", - "iwl5000-firmware", - "iwl5150-firmware", - "iwl6050-firmware", - }, - }, - BIOS: true, - UEFIVendor: rd.Vendor(), - } - - x86_64.AddImageTypes( - bareMetalX86Platform, - mkEdgeOCIImgType(rd, arch.ARCH_X86_64), - mkEdgeCommitImgType(rd, arch.ARCH_X86_64), - mkEdgeInstallerImgType(rd, arch.ARCH_X86_64), - mkImageInstaller(rd, arch.ARCH_X86_64), - ) - - gceX86Platform := &platform.X86{ - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_GCE, - }, - } - - x86_64.AddImageTypes( - gceX86Platform, - mkGceImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_VMDK, - }, - }, - mkVmdkImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_OVA, - }, - }, - mkOvaImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - &platform.X86{}, - mkTarImgType(), - mkWslImgType(rd, arch.ARCH_X86_64), - ) - - aarch64.AddImageTypes( - &platform.Aarch64{ - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - QCOW2Compat: "0.10", - }, - }, - mkQcow2ImgType(rd, arch.ARCH_AARCH64), - ) - - aarch64.AddImageTypes( - &platform.Aarch64{ - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - }, - }, - mkOpenstackImgType(rd, arch.ARCH_AARCH64), - ) - - aarch64.AddImageTypes( - &platform.Aarch64{}, - mkTarImgType(), - mkWslImgType(rd, arch.ARCH_AARCH64), - ) - - bareMetalAarch64Platform := &platform.Aarch64{ - BasePlatform: platform.BasePlatform{}, - UEFIVendor: rd.Vendor(), - } - - aarch64.AddImageTypes( - bareMetalAarch64Platform, - mkEdgeOCIImgType(rd, arch.ARCH_AARCH64), - mkEdgeCommitImgType(rd, arch.ARCH_AARCH64), - mkEdgeInstallerImgType(rd, arch.ARCH_AARCH64), - mkImageInstaller(rd, arch.ARCH_AARCH64), - ) - - rawAarch64Platform := &platform.Aarch64{ - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_RAW, - }, - } - - aarch64.AddImageTypes( - rawAarch64Platform, - mkAmiImgTypeAarch64(rd), - mkMinimalRawImgType(rd, arch.ARCH_AARCH64), - ) - - ppc64le.AddImageTypes( - &platform.PPC64LE{ - BIOS: true, - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - QCOW2Compat: "0.10", - }, - }, - mkQcow2ImgType(rd, arch.ARCH_PPC64LE), - ) - - ppc64le.AddImageTypes( - &platform.PPC64LE{}, - mkTarImgType(), - ) - - s390x.AddImageTypes( - &platform.S390X{ - Zipl: true, - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_QCOW2, - QCOW2Compat: "0.10", - }, - }, - mkQcow2ImgType(rd, arch.ARCH_S390X), - ) - - s390x.AddImageTypes( - &platform.S390X{}, - mkTarImgType(), - ) - - azureX64Platform := &platform.X86{ - BIOS: true, - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_VHD, - }, - } - - azureAarch64Platform := &platform.Aarch64{ - UEFIVendor: rd.Vendor(), - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_VHD, - }, - } - - rawUEFIx86Platform := &platform.X86{ - BasePlatform: platform.BasePlatform{ - ImageFormat: platform.FORMAT_RAW, - }, - BIOS: false, - UEFIVendor: rd.Vendor(), - } - - x86_64.AddImageTypes( - rawUEFIx86Platform, - mkMinimalRawImgType(rd, arch.ARCH_X86_64), - ) - - // XXX: note that this is reduandant and the else part can be dropped, - // we have only rhel8 based images, no centos or others - if rd.IsRHEL() { - if common.VersionGreaterThanOrEqual(rd.OsVersion(), "8.6") { - // image types only available on 8.6 and later on RHEL - // These edge image types require FDO which aren't available on older versions - x86_64.AddImageTypes( - bareMetalX86Platform, - mkEdgeRawImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - rawUEFIx86Platform, - mkEdgeSimplifiedInstallerImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - azureX64Platform, - mkAzureEap7RhuiImgType(rd, arch.ARCH_X86_64), - ) - - aarch64.AddImageTypes( - rawAarch64Platform, - mkEdgeRawImgType(rd, arch.ARCH_AARCH64), - mkEdgeSimplifiedInstallerImgType(rd, arch.ARCH_AARCH64), - ) - - // The Azure image types require hyperv-daemons which isn't available on older versions - aarch64.AddImageTypes( - azureAarch64Platform, - mkAzureRhuiImgType(rd, arch.ARCH_AARCH64), - mkAzureByosImgType(rd, arch.ARCH_AARCH64), - ) - } - - // add azure to RHEL distro only - x86_64.AddImageTypes( - azureX64Platform, - mkAzureRhuiImgType(rd, arch.ARCH_X86_64), - mkAzureByosImgType(rd, arch.ARCH_X86_64), - mkAzureSapRhuiImgType(rd, arch.ARCH_X86_64), - ) - - // add ec2 image types to RHEL distro only - x86_64.AddImageTypes( - ec2X86Platform, - mkEc2ImgTypeX86_64(rd), - mkEc2HaImgTypeX86_64(rd), - ) - aarch64.AddImageTypes( - rawAarch64Platform, - mkEc2ImgTypeAarch64(rd), - ) - - if rd.OsVersion() != "8.5" { - // NOTE: RHEL 8.5 is going away and these image types require some - // work to get working, so we just disable them here until the - // whole distro gets deleted - x86_64.AddImageTypes( - ec2X86Platform, - mkEc2SapImgTypeX86_64(rd), - ) - } - - // add GCE RHUI image to RHEL only - x86_64.AddImageTypes( - gceX86Platform, - mkGceRhuiImgType(rd, arch.ARCH_X86_64), - ) - - // add s390x to RHEL distro only - rd.AddArches(s390x) - } else { - x86_64.AddImageTypes( - bareMetalX86Platform, - mkEdgeRawImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - rawUEFIx86Platform, - mkEdgeSimplifiedInstallerImgType(rd, arch.ARCH_X86_64), - ) - - x86_64.AddImageTypes( - azureX64Platform, - mkAzureImgType(rd, arch.ARCH_X86_64), - ) - - aarch64.AddImageTypes( - rawAarch64Platform, - mkEdgeRawImgType(rd, arch.ARCH_AARCH64), - mkEdgeSimplifiedInstallerImgType(rd, arch.ARCH_AARCH64), - ) - - aarch64.AddImageTypes( - azureAarch64Platform, - mkAzureImgType(rd, arch.ARCH_AARCH64), - ) - } - rd.AddArches(x86_64, aarch64, ppc64le) - return rd -} - -func ParseID(idStr string) (*distro.ID, error) { - id, err := distro.ParseID(idStr) - if err != nil { - return nil, err - } - - if id.Name != "rhel" && id.Name != "centos" && id.Name != "almalinux" { - return nil, fmt.Errorf("invalid distro name: %s", id.Name) - } - - // Backward compatibility layer for "rhel-84" or "rhel-810" - if id.Name == "rhel" && id.MinorVersion == -1 { - if id.MajorVersion/10 == 8 { - // handle single digit minor version - id.MinorVersion = id.MajorVersion % 10 - id.MajorVersion = 8 - } else if id.MajorVersion/100 == 8 { - // handle two digit minor version - id.MinorVersion = id.MajorVersion % 100 - id.MajorVersion = 8 - } - } - - if id.MajorVersion != 8 { - return nil, fmt.Errorf("invalid distro major version: %d", id.MajorVersion) - } - - // CentOS does not use minor version - if id.Name == "centos" && id.MinorVersion != -1 { - return nil, fmt.Errorf("centos does not use minor version, but got: %d", id.MinorVersion) - } - - // RHEL uses minor version - if id.Name == "rhel" && id.MinorVersion == -1 { - return nil, fmt.Errorf("rhel requires minor version, but got: %d", id.MinorVersion) - } - - // So does AlmaLinux - if id.Name == "almalinux" && id.MinorVersion == -1 { - return nil, fmt.Errorf("almalinux requires minor version, but got: %d", id.MinorVersion) - } - - return id, nil -} - -func DistroFactory(idStr string) distro.Distro { - id, err := ParseID(idStr) - if err != nil { - return nil - } - - return newDistro(id.Name, id.MinorVersion) -} diff --git a/pkg/distro/rhel/rhel8/distro_internal_test.go b/pkg/distro/rhel/rhel8/distro_internal_test.go deleted file mode 100644 index 6c2ddf9918..0000000000 --- a/pkg/distro/rhel/rhel8/distro_internal_test.go +++ /dev/null @@ -1,182 +0,0 @@ -package rhel8 - -import ( - "fmt" - "math/rand" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/osbuild/images/pkg/blueprint" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/rhel" -) - -// math/rand is good enough in this case -/* #nosec G404 */ -var rng = rand.New(rand.NewSource(0)) - -func TestEC2Partitioning(t *testing.T) { - testCases := []struct { - distro string - aarch64bootSizeMiB uint64 - }{ - { - distro: "rhel-8.8", - aarch64bootSizeMiB: 512, - }, - { - distro: "rhel-8.9", - aarch64bootSizeMiB: 512, - }, - { - distro: "rhel-8.10", - aarch64bootSizeMiB: 1024, - }, - { - distro: "centos-8", - aarch64bootSizeMiB: 1024, - }, - } - - for _, tt := range testCases { - for _, arch := range []string{"x86_64", "aarch64"} { - for _, it := range []string{"ami", "ec2", "ec2-ha", "ec2-sap"} { - // skip non-existing combos - if strings.HasPrefix(it, "ec2") && strings.HasPrefix(tt.distro, "centos") { - continue - } - if arch == "aarch64" && (it == "ec2-ha" || it == "ec2-sap") { - continue - } - t.Run(fmt.Sprintf("%s/%s/%s", tt.distro, arch, it), func(t *testing.T) { - a, err := DistroFactory(tt.distro).GetArch(arch) - require.NoError(t, err) - i, err := a.GetImageType(it) - require.NoError(t, err) - - it := i.(*rhel.ImageType) - pt, err := it.GetPartitionTable(&blueprint.Customizations{}, distro.ImageOptions{}, rng) - require.NoError(t, err) - - // x86_64 is /boot-less, check that - if arch == "x86_64" { - require.Nil(t, err, pt.FindMountable("/boot")) - return - } - - bootSize, err := pt.GetMountpointSize("/boot") - require.NoError(t, err) - require.Equal(t, tt.aarch64bootSizeMiB*datasizes.MiB, bootSize) - }) - - } - } - - } -} - -func TestDistroFactory(t *testing.T) { - type testCase struct { - strID string - expected distro.Distro - } - - testCases := []testCase{ - { - strID: "rhel-8.0", - expected: newDistro("rhel", 0), - }, - { - strID: "rhel-80", - expected: newDistro("rhel", 0), - }, - { - strID: "rhel-8.4", - expected: newDistro("rhel", 4), - }, - { - strID: "rhel-84", - expected: newDistro("rhel", 4), - }, - { - strID: "rhel-8.10", - expected: newDistro("rhel", 10), - }, - { - strID: "rhel-810", - expected: newDistro("rhel", 10), - }, - { - strID: "centos-8", - expected: newDistro("centos", -1), - }, - { - strID: "centos-8.4", - expected: nil, - }, - { - strID: "rhel-8", - expected: nil, - }, - { - strID: "rhel-8.4.1", - expected: nil, - }, - { - strID: "rhel-7", - expected: nil, - }, - { - strID: "rhel-79", - expected: nil, - }, - { - strID: "rhel-7.9", - expected: nil, - }, - { - strID: "fedora-8", - expected: nil, - }, - { - strID: "fedora-38", - expected: nil, - }, - { - strID: "fedora-38.1", - expected: nil, - }, - { - strID: "fedora", - expected: nil, - }, - { - strID: "rhel-9", - expected: nil, - }, - { - strID: "rhel-910", - expected: nil, - }, - { - strID: "rhel-9.10", - expected: nil, - }, - } - - for _, tc := range testCases { - t.Run(tc.strID, func(t *testing.T) { - d := DistroFactory(tc.strID) - if tc.expected == nil { - assert.Nil(t, d) - } else { - assert.NotNil(t, d) - assert.Equal(t, tc.expected.Name(), d.Name()) - } - }) - } -} diff --git a/pkg/distro/rhel/rhel8/distro_test.go b/pkg/distro/rhel/rhel8/distro_test.go deleted file mode 100644 index 489416e21c..0000000000 --- a/pkg/distro/rhel/rhel8/distro_test.go +++ /dev/null @@ -1,985 +0,0 @@ -package rhel8_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/blueprint" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/distro_test_common" - "github.com/osbuild/images/pkg/distro/rhel/rhel8" -) - -type rhelFamilyDistro struct { - name string - distro distro.Distro -} - -var rhelFamilyDistros = []rhelFamilyDistro{ - { - name: "rhel-810", - distro: rhel8.DistroFactory("rhel-810"), - }, -} - -func TestFilenameFromType(t *testing.T) { - type args struct { - outputFormat string - } - type wantResult struct { - filename string - mimeType string - wantErr bool - } - tests := []struct { - name string - args args - want wantResult - }{ - { - name: "ami", - args: args{"ami"}, - want: wantResult{ - filename: "image.raw", - mimeType: "application/octet-stream", - }, - }, - { - name: "ec2", - args: args{"ec2"}, - want: wantResult{ - filename: "image.raw.xz", - mimeType: "application/xz", - }, - }, - { - name: "ec2-ha", - args: args{"ec2-ha"}, - want: wantResult{ - filename: "image.raw.xz", - mimeType: "application/xz", - }, - }, - { - name: "ec2-sap", - args: args{"ec2-sap"}, - want: wantResult{ - filename: "image.raw.xz", - mimeType: "application/xz", - }, - }, - { - name: "qcow2", - args: args{"qcow2"}, - want: wantResult{ - filename: "disk.qcow2", - mimeType: "application/x-qemu-disk", - }, - }, - { - name: "openstack", - args: args{"openstack"}, - want: wantResult{ - filename: "disk.qcow2", - mimeType: "application/x-qemu-disk", - }, - }, - { - name: "vhd", - args: args{"vhd"}, - want: wantResult{ - filename: "disk.vhd", - mimeType: "application/x-vhd", - }, - }, - { - name: "azure-rhui", - args: args{"azure-rhui"}, - want: wantResult{ - filename: "disk.vhd.xz", - mimeType: "application/xz", - }, - }, - { - name: "azure-sap-rhui", - args: args{"azure-sap-rhui"}, - want: wantResult{ - filename: "disk.vhd.xz", - mimeType: "application/xz", - }, - }, - { - name: "vmdk", - args: args{"vmdk"}, - want: wantResult{ - filename: "disk.vmdk", - mimeType: "application/x-vmdk", - }, - }, - { - name: "ova", - args: args{"ova"}, - want: wantResult{ - filename: "image.ova", - mimeType: "application/ovf", - }, - }, - { - name: "tar", - args: args{"tar"}, - want: wantResult{ - filename: "root.tar.xz", - mimeType: "application/x-tar", - }, - }, - { - name: "image-installer", - args: args{"image-installer"}, - want: wantResult{ - filename: "installer.iso", - mimeType: "application/x-iso9660-image", - }, - }, - { - name: "edge-commit", - args: args{"edge-commit"}, - want: wantResult{ - filename: "commit.tar", - mimeType: "application/x-tar", - }, - }, - // Alias - { - name: "rhel-edge-commit", - args: args{"rhel-edge-commit"}, - want: wantResult{ - filename: "commit.tar", - mimeType: "application/x-tar", - }, - }, - { - name: "edge-container", - args: args{"edge-container"}, - want: wantResult{ - filename: "container.tar", - mimeType: "application/x-tar", - }, - }, - // Alias - { - name: "rhel-edge-container", - args: args{"rhel-edge-container"}, - want: wantResult{ - filename: "container.tar", - mimeType: "application/x-tar", - }, - }, - { - name: "edge-installer", - args: args{"edge-installer"}, - want: wantResult{ - filename: "installer.iso", - mimeType: "application/x-iso9660-image", - }, - }, - // Alias - { - name: "rhel-edge-installer", - args: args{"rhel-edge-installer"}, - want: wantResult{ - filename: "installer.iso", - mimeType: "application/x-iso9660-image", - }, - }, - { - name: "gce", - args: args{"gce"}, - want: wantResult{ - filename: "image.tar.gz", - mimeType: "application/gzip", - }, - }, - { - name: "gce-rhui", - args: args{"gce-rhui"}, - want: wantResult{ - filename: "image.tar.gz", - mimeType: "application/gzip", - }, - }, - { - name: "invalid-output-type", - args: args{"foobar"}, - want: wantResult{wantErr: true}, - }, - { - name: "minimal-raw", - args: args{"minimal-raw"}, - want: wantResult{ - filename: "disk.raw.xz", - mimeType: "application/xz", - }, - }, - } - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dist := dist.distro - arch, _ := dist.GetArch("x86_64") - imgType, err := arch.GetImageType(tt.args.outputFormat) - if tt.want.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.NotNil(t, imgType) - gotFilename := imgType.Filename() - gotMIMEType := imgType.MIMEType() - if gotFilename != tt.want.filename { - t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename) - } - if gotMIMEType != tt.want.mimeType { - t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType) - } - } - }) - } - }) - } -} - -func TestImageType_BuildPackages(t *testing.T) { - x8664BuildPackages := []string{ - "dnf", - "dosfstools", - "e2fsprogs", - "grub2-efi-x64", - "grub2-pc", - "policycoreutils", - "shim-x64", - "systemd", - "tar", - "qemu-img", - "xz", - } - aarch64BuildPackages := []string{ - "dnf", - "dosfstools", - "e2fsprogs", - "policycoreutils", - "qemu-img", - "systemd", - "tar", - "xz", - } - buildPackages := map[string][]string{ - "x86_64": x8664BuildPackages, - "aarch64": aarch64BuildPackages, - } - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - d := dist.distro - for _, archLabel := range d.ListArches() { - archStruct, err := d.GetArch(archLabel) - if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) { - continue - } - for _, itLabel := range archStruct.ListImageTypes() { - itStruct, err := archStruct.GetImageType(itLabel) - if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) { - continue - } - manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, nil) - assert.NoError(t, err) - buildPkgs := manifest.GetPackageSetChains()["build"] - assert.NotNil(t, buildPkgs) - assert.Len(t, buildPkgs, 1) - assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include) - } - } - }) - } -} - -func TestImageType_Name(t *testing.T) { - imgMap := []struct { - arch string - imgNames []string - }{ - { - arch: "x86_64", - imgNames: []string{ - "qcow2", - "openstack", - "vhd", - "azure-rhui", - "azure-sap-rhui", - "azure-eap7-rhui", - "vmdk", - "ova", - "ami", - "ec2", - "ec2-ha", - "ec2-sap", - "gce", - "gce-rhui", - "edge-commit", - "edge-container", - "edge-installer", - "tar", - "image-installer", - "minimal-raw", - }, - }, - { - arch: "aarch64", - imgNames: []string{ - "qcow2", - "openstack", - "vhd", - "azure-rhui", - "ami", - "ec2", - "edge-commit", - "edge-container", - "tar", - "minimal-raw", - }, - }, - { - arch: "ppc64le", - imgNames: []string{ - "qcow2", - "tar", - }, - }, - { - arch: "s390x", - imgNames: []string{ - "qcow2", - "tar", - }, - }, - } - - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - for _, mapping := range imgMap { - if mapping.arch == arch.ARCH_S390X.String() && dist.name == "centos" { - continue - } - arch, err := dist.distro.GetArch(mapping.arch) - if assert.NoError(t, err) { - for _, imgName := range mapping.imgNames { - if imgName == "edge-commit" && dist.name == "centos" { - continue - } - imgType, err := arch.GetImageType(imgName) - if assert.NoError(t, err) { - assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch) - } - } - } - } - }) - } -} - -func TestImageTypeAliases(t *testing.T) { - type args struct { - imageTypeAliases []string - } - type wantResult struct { - imageTypeName string - } - tests := []struct { - name string - args args - want wantResult - }{ - { - name: "edge-commit aliases", - args: args{ - imageTypeAliases: []string{"rhel-edge-commit"}, - }, - want: wantResult{ - imageTypeName: "edge-commit", - }, - }, - { - name: "edge-container aliases", - args: args{ - imageTypeAliases: []string{"rhel-edge-container"}, - }, - want: wantResult{ - imageTypeName: "edge-container", - }, - }, - { - name: "edge-installer aliases", - args: args{ - imageTypeAliases: []string{"rhel-edge-installer"}, - }, - want: wantResult{ - imageTypeName: "edge-installer", - }, - }, - } - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dist := dist.distro - for _, archName := range dist.ListArches() { - t.Run(archName, func(t *testing.T) { - arch, err := dist.GetArch(archName) - require.Nilf(t, err, - "failed to get architecture '%s', previously listed as supported for the distro '%s'", - archName, dist.Name()) - // Test image type aliases only if the aliased image type is supported for the arch - if _, err = arch.GetImageType(tt.want.imageTypeName); err != nil { - t.Skipf("aliased image type '%s' is not supported for architecture '%s'", - tt.want.imageTypeName, archName) - } - for _, alias := range tt.args.imageTypeAliases { - t.Run(fmt.Sprintf("'%s' alias for image type '%s'", alias, tt.want.imageTypeName), - func(t *testing.T) { - gotImage, err := arch.GetImageType(alias) - require.Nilf(t, err, "arch.GetImageType() for image type alias '%s' failed: %v", - alias, err) - assert.Equalf(t, tt.want.imageTypeName, gotImage.Name(), - "got unexpected image type name for alias '%s'. got = %s, want = %s", - alias, tt.want.imageTypeName, gotImage.Name()) - }) - } - }) - } - }) - } - }) - } -} - -// Check that Manifest() function returns an error for unsupported -// configurations. -func TestDistro_ManifestError(t *testing.T) { - // Currently, the only unsupported configuration is OSTree commit types - // with Kernel boot options - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Kernel: &blueprint.KernelCustomization{ - Append: "debug", - }, - }, - } - - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - imgOpts := distro.ImageOptions{ - Size: imgType.Size(0), - } - _, _, err := imgType.Manifest(&bp, imgOpts, nil, nil) - if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { - assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types") - } else if imgTypeName == "edge-raw-image" { - assert.EqualError(t, err, fmt.Sprintf("%q images require specifying a URL from which to retrieve the OSTree commit", imgTypeName)) - } else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" { - assert.EqualError(t, err, fmt.Sprintf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", imgTypeName)) - } else { - assert.NoError(t, err) - } - } - } -} - -func TestArchitecture_ListImageTypes(t *testing.T) { - imgMap := []struct { - arch string - imgNames []string - rhelAdditionalImageTypes []string - }{ - { - arch: "x86_64", - imgNames: []string{ - "qcow2", - "openstack", - "vhd", - "azure-rhui", - "azure-sap-rhui", - "azure-eap7-rhui", - "vmdk", - "ova", - "ami", - "ec2", - "ec2-ha", - "ec2-sap", - "gce", - "gce-rhui", - "edge-commit", - "edge-container", - "edge-installer", - "edge-raw-image", - "edge-simplified-installer", - "tar", - "image-installer", - "oci", - "wsl", - "minimal-raw", - }, - }, - { - arch: "aarch64", - imgNames: []string{ - "qcow2", - "openstack", - "vhd", - "azure-rhui", - "ami", - "ec2", - "edge-commit", - "edge-container", - "edge-installer", - "edge-simplified-installer", - "edge-raw-image", - "tar", - "image-installer", - "wsl", - "minimal-raw", - }, - }, - { - arch: "ppc64le", - imgNames: []string{ - "qcow2", - "tar", - }, - }, - { - arch: "s390x", - imgNames: []string{ - "qcow2", - "tar", - }, - }, - } - - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - for _, mapping := range imgMap { - arch, err := dist.distro.GetArch(mapping.arch) - require.NoError(t, err) - imageTypes := arch.ListImageTypes() - - var expectedImageTypes []string - expectedImageTypes = append(expectedImageTypes, mapping.imgNames...) - if dist.name == "rhel" { - expectedImageTypes = append(expectedImageTypes, mapping.rhelAdditionalImageTypes...) - } - - require.ElementsMatch(t, expectedImageTypes, imageTypes) - } - }) - } -} - -func TestRHEL8_ListArches(t *testing.T) { - arches := rhelFamilyDistros[0].distro.ListArches() - assert.Equal(t, []string{"aarch64", "ppc64le", "s390x", "x86_64"}, arches) -} - -func TestRHEL8_GetArch(t *testing.T) { - arches := []struct { - name string - errorExpected bool - errorExpectedInCentos bool - }{ - { - name: "x86_64", - }, - { - name: "aarch64", - }, - { - name: "ppc64le", - }, - { - name: "s390x", - }, - { - name: "foo-arch", - errorExpected: true, - }, - } - - for _, dist := range rhelFamilyDistros { - t.Run(dist.name, func(t *testing.T) { - for _, a := range arches { - actualArch, err := dist.distro.GetArch(a.name) - if a.errorExpected || (a.errorExpectedInCentos && dist.name == "centos") { - assert.Nil(t, actualArch) - assert.Error(t, err) - } else { - assert.Equal(t, a.name, actualArch.Name()) - assert.NoError(t, err) - } - } - }) - } -} - -func TestRhel8_Name(t *testing.T) { - distro := rhelFamilyDistros[0].distro - assert.Equal(t, "rhel-8.10", distro.Name()) -} - -func TestRhel8_ModulePlatformID(t *testing.T) { - distro := rhelFamilyDistros[0].distro - assert.Equal(t, "platform:el8", distro.ModulePlatformID()) -} - -func TestRhel86_KernelOption(t *testing.T) { - distro_test_common.TestDistro_KernelOption(t, rhelFamilyDistros[0].distro) -} - -func TestRhel8_OSTreeOptions(t *testing.T) { - distro_test_common.TestDistro_OSTreeOptions(t, rhelFamilyDistros[0].distro) -} - -func TestDistro_CustomFileSystemManifestError(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "/etc", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { - assert.EqualError(t, err, "custom mountpoints are not supported for ostree types") - } else if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.EqualError(t, err, "The following errors occurred while setting up custom mountpoints:\npath \"/etc\" is not allowed") - } - } - } -} - -func TestDistro_TestRootMountPoint(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "/", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { - assert.EqualError(t, err, "custom mountpoints are not supported for ostree types") - } else if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } - } -} - -func TestDistro_CustomFileSystemSubDirectories(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "/var/log", - }, - { - MinSize: 1024, - Mountpoint: "/var/log/audit", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-commit": true, - "edge-container": true, - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } - } -} - -func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "/var/a", - }, - { - MinSize: 1024, - Mountpoint: "/var/a/b", - }, - { - MinSize: 1024, - Mountpoint: "/var/a/b/c", - }, - { - MinSize: 1024, - Mountpoint: "/var/a/b/c/d", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-commit": true, - "edge-container": true, - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } - } -} - -func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "//", - }, - { - MinSize: 1024, - Mountpoint: "/var//", - }, - { - MinSize: 1024, - Mountpoint: "/var//log/audit/", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-commit": true, - "edge-container": true, - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.EqualError(t, err, "The following errors occurred while setting up custom mountpoints:\npath \"//\" must be canonical\npath \"/var//\" must be canonical\npath \"/var//log/audit/\" must be canonical") - } - } - } -} - -func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - bp := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Filesystem: []blueprint.FilesystemCustomization{ - { - MinSize: 1024, - Mountpoint: "/usr", - }, - }, - }, - } - unsupported := map[string]bool{ - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - for _, archName := range r8distro.ListArches() { - arch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range arch.ListImageTypes() { - imgType, _ := arch.GetImageType(imgTypeName) - _, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, nil) - if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { - assert.EqualError(t, err, "custom mountpoints are not supported for ostree types") - } else if unsupported[imgTypeName] { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } - } -} - -func TestDiskCustomizationsCheckOptions(t *testing.T) { - r8distro := rhelFamilyDistros[0].distro - plainBP := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Disk: &blueprint.DiskCustomization{ - Partitions: []blueprint.PartitionCustomization{ - { - Type: "plain", - FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ - Mountpoint: "/", - Label: "root", - FSType: "ext4", - }, - }, - }, - }, - }, - } - - bpWithSwap := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Disk: &blueprint.DiskCustomization{ - Partitions: []blueprint.PartitionCustomization{ - { - Type: "plain", - FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ - Mountpoint: "/", - Label: "root", - FSType: "ext4", - }, - }, - { - Type: "plain", - FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ - FSType: "swap", - }, - }, - }, - }, - }, - } - - bpWithSwapLV := blueprint.Blueprint{ - Customizations: &blueprint.Customizations{ - Disk: &blueprint.DiskCustomization{ - Partitions: []blueprint.PartitionCustomization{ - { - Type: "lvm", - VGCustomization: blueprint.VGCustomization{ - LogicalVolumes: []blueprint.LVCustomization{ - { - FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ - Mountpoint: "/", - Label: "root", - FSType: "ext4", - }, - }, - { - FilesystemTypedCustomization: blueprint.FilesystemTypedCustomization{ - FSType: "swap", - }, - }, - }, - }, - }, - }, - }, - }, - } - - // these produce a different error message and are tested elsewhere - skipTest := map[string]bool{ - "edge-installer": true, - "edge-simplified-installer": true, - "edge-raw-image": true, - } - - for _, archName := range r8distro.ListArches() { - distroArch, _ := r8distro.GetArch(archName) - for _, imgTypeName := range distroArch.ListImageTypes() { - imgType, _ := distroArch.GetImageType(imgTypeName) - if skipTest[imgTypeName] { - continue - } - - testname := fmt.Sprintf("%s-%s-%s", r8distro.Name(), archName, imgTypeName) - t.Run(testname, func(t *testing.T) { - assert := assert.New(t) - - _, _, err := imgType.Manifest(&plainBP, distro.ImageOptions{}, nil, nil) - assert.NoError(err) - - _, _, err = imgType.Manifest(&bpWithSwap, distro.ImageOptions{}, nil, nil) - if archName == arch.ARCH_AARCH64.String() { - assert.EqualError(err, fmt.Sprintf("swap partition creation is not supported on %s %s", r8distro.Name(), archName)) - } else { - assert.NoError(err) - } - - _, _, err = imgType.Manifest(&bpWithSwapLV, distro.ImageOptions{}, nil, nil) - if archName == arch.ARCH_AARCH64.String() { - assert.EqualError(err, fmt.Sprintf("swap partition creation is not supported on %s %s", r8distro.Name(), archName)) - } else { - assert.NoError(err) - } - }) - } - } -} diff --git a/pkg/distro/rhel/rhel8/edge.go b/pkg/distro/rhel/rhel8/edge.go deleted file mode 100644 index 1eeebd6f9f..0000000000 --- a/pkg/distro/rhel/rhel8/edge.go +++ /dev/null @@ -1,155 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/disk" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkEdgeCommitImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "edge-commit", - "commit.tar", - "application/x-tar", - packageSetLoader, - rhel.EdgeCommitImage, - []string{"build"}, - []string{"os", "ostree-commit", "commit-archive"}, - []string{"commit-archive"}, - ) - - it.NameAliases = []string{"rhel-edge-commit"} - it.DefaultImageConfig = imageConfig(rd, a.String(), "edge-commit") - it.RPMOSTree = true - - return it -} - -func mkEdgeOCIImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "edge-container", - "container.tar", - "application/x-tar", - packageSetLoader, - rhel.EdgeContainerImage, - []string{"build"}, - []string{"os", "ostree-commit", "container-tree", "container"}, - []string{"container"}, - ) - - it.NameAliases = []string{"rhel-edge-container"} - it.DefaultImageConfig = imageConfig(rd, a.String(), "edge-container") - it.RPMOSTree = true - - return it -} - -func mkEdgeRawImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "edge-raw-image", - "image.raw.xz", - "application/xz", - nil, - rhel.EdgeRawImage, - []string{"build"}, - []string{"ostree-deployment", "image", "xz"}, - []string{"xz"}, - ) - - it.NameAliases = []string{"rhel-edge-raw-image"} - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, a.String(), "edge-raw-image") - it.DefaultSize = 10 * datasizes.GibiByte - it.RPMOSTree = true - it.Bootable = true - it.BasePartitionTables = partitionTables - it.UnsupportedPartitioningModes = []disk.PartitioningMode{ - disk.AutoLVMPartitioningMode, - disk.LVMPartitioningMode, - } - - return it -} - -func mkEdgeInstallerImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "edge-installer", - "installer.iso", - "application/x-iso9660-image", - packageSetLoader, - rhel.EdgeInstallerImage, - []string{"build"}, - []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"}, - []string{"bootiso"}, - ) - - it.NameAliases = []string{"rhel-edge-installer"} - it.DefaultImageConfig = imageConfig(rd, a.String(), "edge-installer") - it.DefaultInstallerConfig = &distro.InstallerConfig{ - AdditionalDracutModules: []string{ - "ifcfg", - }, - } - it.RPMOSTree = true - it.BootISO = true - it.ISOLabelFn = distroISOLabelFunc - - return it -} - -func mkEdgeSimplifiedInstallerImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "edge-simplified-installer", - "simplified-installer.iso", - "application/x-iso9660-image", - packageSetLoader, - rhel.EdgeSimplifiedInstallerImage, - []string{"build"}, - []string{"ostree-deployment", "image", "xz", "coi-tree", "efiboot-tree", "bootiso-tree", "bootiso"}, - []string{"bootiso"}, - ) - - it.NameAliases = []string{"rhel-edge-simplified-installer"} - it.DefaultImageConfig = imageConfig(rd, a.String(), "edge-simplified-installer") - it.DefaultInstallerConfig = &distro.InstallerConfig{ - AdditionalDracutModules: []string{ - "prefixdevname", - "prefixdevname-tools", - }, - } - it.DefaultSize = 10 * datasizes.GibiByte - it.RPMOSTree = true - it.Bootable = true - it.BootISO = true - it.ISOLabelFn = distroISOLabelFunc - it.BasePartitionTables = partitionTables - it.UnsupportedPartitioningModes = []disk.PartitioningMode{ - disk.AutoLVMPartitioningMode, - disk.LVMPartitioningMode, - } - - return it -} - -func mkMinimalRawImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "minimal-raw", - "disk.raw.xz", - "application/xz", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "xz"}, - []string{"xz"}, - ) - - it.Compression = "xz" - it.DefaultImageConfig = imageConfig(rd, a.String(), "minimal-raw") - it.Bootable = true - it.DefaultSize = 2 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/gce.go b/pkg/distro/rhel/rhel8/gce.go deleted file mode 100644 index 9eb546a74b..0000000000 --- a/pkg/distro/rhel/rhel8/gce.go +++ /dev/null @@ -1,49 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkGceImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "gce", - "image.tar.gz", - "application/gzip", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "archive"}, - []string{"archive"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "gce") - it.Bootable = true - it.DefaultSize = 20 * datasizes.GibiByte - // TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only - it.BasePartitionTables = partitionTables - - return it -} - -func mkGceRhuiImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "gce-rhui", - "image.tar.gz", - "application/gzip", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "archive"}, - []string{"archive"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "gce-rhui") - it.Bootable = true - it.DefaultSize = 20 * datasizes.GibiByte - // TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/options.go b/pkg/distro/rhel/rhel8/options.go deleted file mode 100644 index 1f6b606824..0000000000 --- a/pkg/distro/rhel/rhel8/options.go +++ /dev/null @@ -1,207 +0,0 @@ -package rhel8 - -import ( - "fmt" - "strings" - - "slices" - - "github.com/osbuild/images/internal/common" - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/blueprint" - "github.com/osbuild/images/pkg/customizations/oscap" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/rhel" - "github.com/osbuild/images/pkg/policies" -) - -// checkOptions checks the validity and compatibility of options and customizations for the image type. -// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices). -func checkOptions(t *rhel.ImageType, bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) { - customizations := bp.Customizations - // holds warnings (e.g. deprecation notices) - var warnings []string - - // we do not support embedding containers on ostree-derived images, only on commits themselves - if len(bp.Containers) > 0 && t.RPMOSTree && (t.Name() != "edge-commit" && t.Name() != "edge-container") { - return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.Name(), t.Arch().Distro().Name()) - } - - if options.OSTree != nil { - if err := options.OSTree.Validate(); err != nil { - return warnings, err - } - } - - if t.BootISO && t.RPMOSTree { - // ostree-based ISOs require a URL from which to pull a payload commit - if options.OSTree == nil || options.OSTree.URL == "" { - return warnings, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.Name()) - } - - if t.Name() == "edge-simplified-installer" { - allowed := []string{"InstallationDevice", "FDO", "User", "Group", "FIPS"} - if err := customizations.CheckAllowed(allowed...); err != nil { - return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) - } - if customizations.GetInstallationDevice() == "" { - return warnings, fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.Name()) - } - //making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint - if customizations.GetFDO() != nil { - if customizations.GetFDO().ManufacturingServerURL == "" { - return warnings, fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.Name()) - } - var diunSet int - if customizations.GetFDO().DiunPubKeyHash != "" { - diunSet++ - } - if customizations.GetFDO().DiunPubKeyInsecure != "" { - diunSet++ - } - if customizations.GetFDO().DiunPubKeyRootCerts != "" { - diunSet++ - } - if diunSet != 1 { - return warnings, fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.Name()) - } - } - } else if t.Name() == "edge-installer" { - allowed := []string{"User", "Group", "FIPS", "Installer", "Timezone", "Locale"} - if err := customizations.CheckAllowed(allowed...); err != nil { - return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) - } - } - } - - if t.Name() == "edge-raw-image" { - // ostree-based bootable images require a URL from which to pull a payload commit - if options.OSTree == nil || options.OSTree.URL == "" { - return warnings, fmt.Errorf("%q images require specifying a URL from which to retrieve the OSTree commit", t.Name()) - } - - allowed := []string{"User", "Group", "FIPS"} - if err := customizations.CheckAllowed(allowed...); err != nil { - return warnings, fmt.Errorf(distro.UnsupportedCustomizationError, t.Name(), strings.Join(allowed, ", ")) - } - // TODO: consider additional checks, such as those in "edge-simplified-installer" - } - - if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.RPMOSTree && t.Name() != "edge-raw-image" && t.Name() != "edge-simplified-installer" { - return warnings, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") - } - - if slices.Contains(t.UnsupportedPartitioningModes, options.PartitioningMode) { - return warnings, fmt.Errorf("partitioning mode %q is not supported for %q", options.PartitioningMode, t.Name()) - } - - mountpoints := customizations.GetFilesystems() - partitioning, err := customizations.GetPartitioning() - if err != nil { - return nil, err - } - - if partitioning != nil { - for _, partition := range partitioning.Partitions { - if t.Arch().Name() == arch.ARCH_AARCH64.String() { - if partition.FSType == "swap" { - return warnings, fmt.Errorf("swap partition creation is not supported on %s %s", t.Arch().Distro().Name(), t.Arch().Name()) - } - for _, lv := range partition.LogicalVolumes { - if lv.FSType == "swap" { - return warnings, fmt.Errorf("swap partition creation is not supported on %s %s", t.Arch().Distro().Name(), t.Arch().Name()) - } - } - } - } - } - - if mountpoints != nil && t.RPMOSTree { - return warnings, fmt.Errorf("custom mountpoints are not supported for ostree types") - } - - if err := blueprint.CheckMountpointsPolicy(mountpoints, policies.MountpointPolicies); err != nil { - return warnings, err - } - - if err := partitioning.ValidateLayoutConstraints(); err != nil { - return warnings, err - } - - if err := blueprint.CheckDiskMountpointsPolicy(partitioning, policies.MountpointPolicies); err != nil { - return warnings, err - } - - if osc := customizations.GetOpenSCAP(); osc != nil { - if t.Arch().Distro().OsVersion() == "9.0" { - return warnings, fmt.Errorf("OpenSCAP unsupported os version: %s", t.Arch().Distro().OsVersion()) - } - if !oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList) { - return warnings, fmt.Errorf("OpenSCAP unsupported profile: %s", osc.ProfileID) - } - if t.RPMOSTree { - return warnings, fmt.Errorf("OpenSCAP customizations are not supported for ostree types") - } - if osc.ProfileID == "" { - return warnings, fmt.Errorf("OpenSCAP profile cannot be empty") - } - } - - // Check Directory/File Customizations are valid - dc := customizations.GetDirectories() - fc := customizations.GetFiles() - - err = blueprint.ValidateDirFileCustomizations(dc, fc) - if err != nil { - return warnings, err - } - - dcp := policies.CustomDirectoriesPolicies - fcp := policies.CustomFilesPolicies - - if t.RPMOSTree { - dcp = policies.OstreeCustomDirectoriesPolicies - fcp = policies.OstreeCustomFilesPolicies - } - - err = blueprint.CheckDirectoryCustomizationsPolicy(dc, dcp) - if err != nil { - return warnings, err - } - - err = blueprint.CheckFileCustomizationsPolicy(fc, fcp) - if err != nil { - return warnings, err - } - - // check if repository customizations are valid - _, err = customizations.GetRepositories() - if err != nil { - return warnings, err - } - - if customizations.GetFIPS() && !common.IsBuildHostFIPSEnabled() { - w := fmt.Sprintln(common.FIPSEnabledImageWarning) - warnings = append(warnings, w) - } - - instCust, err := customizations.GetInstaller() - if err != nil { - return warnings, err - } - if instCust != nil { - // only supported by the Anaconda installer - if slices.Index([]string{"image-installer", "edge-installer", "live-installer"}, t.Name()) == -1 { - return warnings, fmt.Errorf("installer customizations are not supported for %q", t.Name()) - } - - if t.Name() == "edge-installer" && - instCust.Kickstart != nil && - len(instCust.Kickstart.Contents) > 0 && - (customizations.GetUsers() != nil || customizations.GetGroups() != nil) { - return warnings, fmt.Errorf("edge-installer installer.kickstart.contents are not supported in combination with users or groups") - } - } - - return warnings, nil -} diff --git a/pkg/distro/rhel/rhel8/package_sets.go b/pkg/distro/rhel/rhel8/package_sets.go deleted file mode 100644 index c3c37f917e..0000000000 --- a/pkg/distro/rhel/rhel8/package_sets.go +++ /dev/null @@ -1,19 +0,0 @@ -package rhel8 - -// This file defines package sets that are used by more than one image type. - -import ( - "github.com/osbuild/images/internal/common" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/distro/defs" - "github.com/osbuild/images/pkg/distro/rhel" - "github.com/osbuild/images/pkg/rpmmd" -) - -func packageSetLoader(t *rhel.ImageType) (map[string]rpmmd.PackageSet, error) { - return defs.PackageSets(t) -} - -func imageConfig(d *rhel.Distribution, archName, imageType string) *distro.ImageConfig { - return common.Must(defs.ImageConfig(d.Name(), archName, imageType)) -} diff --git a/pkg/distro/rhel/rhel8/partition_tables.go b/pkg/distro/rhel/rhel8/partition_tables.go deleted file mode 100644 index 598f83c31a..0000000000 --- a/pkg/distro/rhel/rhel8/partition_tables.go +++ /dev/null @@ -1,23 +0,0 @@ -package rhel8 - -import ( - "errors" - - "github.com/osbuild/images/pkg/disk" - "github.com/osbuild/images/pkg/distro/defs" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func partitionTables(t *rhel.ImageType) (disk.PartitionTable, bool) { - partitionTable, err := defs.PartitionTable(t) - if errors.Is(err, defs.ErrNoPartitionTableForImgType) { - return disk.PartitionTable{}, false - } - if err != nil { - panic(err) - } - if partitionTable == nil { - return disk.PartitionTable{}, false - } - return *partitionTable, true -} diff --git a/pkg/distro/rhel/rhel8/qcow2.go b/pkg/distro/rhel/rhel8/qcow2.go deleted file mode 100644 index 10229e5a1c..0000000000 --- a/pkg/distro/rhel/rhel8/qcow2.go +++ /dev/null @@ -1,66 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkQcow2ImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "qcow2", - "disk.qcow2", - "application/x-qemu-disk", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "qcow2"}, - []string{"qcow2"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "qcow2") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkOCIImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "oci", - "disk.qcow2", - "application/x-qemu-disk", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "qcow2"}, - []string{"qcow2"}, - ) - - it.DefaultImageConfig = imageConfig(rd, a.String(), "oci") - it.Bootable = true - it.DefaultSize = 10 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkOpenstackImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "openstack", - "disk.qcow2", - "application/x-qemu-disk", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "qcow2"}, - []string{"qcow2"}, - ) - it.DefaultImageConfig = imageConfig(rd, a.String(), "openstack") - it.DefaultSize = 4 * datasizes.GibiByte - it.Bootable = true - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/vmdk.go b/pkg/distro/rhel/rhel8/vmdk.go deleted file mode 100644 index 31475c2133..0000000000 --- a/pkg/distro/rhel/rhel8/vmdk.go +++ /dev/null @@ -1,45 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/datasizes" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkVmdkImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "vmdk", - "disk.vmdk", - "application/x-vmdk", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vmdk"}, - []string{"vmdk"}, - ) - it.DefaultImageConfig = imageConfig(rd, a.String(), "vmdk") - it.Bootable = true - it.DefaultSize = 4 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} - -func mkOvaImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "ova", - "image.ova", - "application/ovf", - packageSetLoader, - rhel.DiskImage, - []string{"build"}, - []string{"os", "image", "vmdk", "ovf", "archive"}, - []string{"archive"}, - ) - it.DefaultImageConfig = imageConfig(rd, a.String(), "ova") - it.Bootable = true - it.DefaultSize = 4 * datasizes.GibiByte - it.BasePartitionTables = partitionTables - - return it -} diff --git a/pkg/distro/rhel/rhel8/wsl.go b/pkg/distro/rhel/rhel8/wsl.go deleted file mode 100644 index a411d48d8b..0000000000 --- a/pkg/distro/rhel/rhel8/wsl.go +++ /dev/null @@ -1,23 +0,0 @@ -package rhel8 - -import ( - "github.com/osbuild/images/pkg/arch" - "github.com/osbuild/images/pkg/distro/rhel" -) - -func mkWslImgType(rd *rhel.Distribution, a arch.Arch) *rhel.ImageType { - it := rhel.NewImageType( - "wsl", - "image.wsl", - "application/x-tar", - packageSetLoader, - rhel.TarImage, - []string{"build"}, - []string{"os", "archive", "xz"}, - []string{"xz"}, - ) - it.DefaultImageConfig = imageConfig(rd, a.String(), "wsl") - it.Compression = "xz" - - return it -} From 06e2c5e7a767cf509866abcd212e1ba6a162e582 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 18 Jul 2025 16:17:24 +0200 Subject: [PATCH 12/14] distro: describe new `platforms_override` Describe the new `platforms_override` key and also document the new `not_distro_name` condition. Thanks to bcl for suggesting this. --- pkg/distro/defs/README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/distro/defs/README.md b/pkg/distro/defs/README.md index 02c00cff49..6e91ba030c 100644 --- a/pkg/distro/defs/README.md +++ b/pkg/distro/defs/README.md @@ -83,6 +83,12 @@ Conditions can be used and *only* the "append" action is supported, this means that the packages from the conditions is appended to the original package sets. +### platforms_override + +This can be used to override the platforms for the image type based +on some condition. See the rhel-8 "ami" image type for an example +where the `aarch64` architecture is only available for rhel-8.9+. + ### conditions Conditions are expressed using the following form: @@ -112,11 +118,12 @@ shared and merge via the `<<:` operation. The `when` part of the condition can contain one or more of: -- distro_name -- arch -- version_less_than -- version_equal -- version_greater +- `distro_name` +- `not_distro_name` +- `arch` +- `version_less_than` +- `version_equal` +- `version_greater` If multiple conditions are given under `when` they are considered logical AND and only if they all match is the condition executed. From 57466181e8851300a5385aa2ba60f5dbfe1066f7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 21 Jul 2025 17:10:32 +0200 Subject: [PATCH 13/14] distro: add missing bootstrap_containers for rhel8 --- pkg/distro/defs/distros.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/distro/defs/distros.yaml b/pkg/distro/defs/distros.yaml index 593a6a5bf3..cfed85c415 100644 --- a/pkg/distro/defs/distros.yaml +++ b/pkg/distro/defs/distros.yaml @@ -195,6 +195,11 @@ distros: # Install python36 explicitly for RHEL 8. - "python36" oscap_profiles_allowlist: *oscap_profile_allowlist_rhel + bootstrap_containers: + x86_64: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" + aarch64: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" + ppc64le: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" + s390x: "registry.access.redhat.com/ubi{{.MajorVersion}}/ubi:latest" - ¢os8 <<: *rhel8 @@ -209,6 +214,11 @@ distros: name: org.osbuild.centos8 build_packages: *rhel8_runner_build_packages oscap_profiles_allowlist: *oscap_profile_allowlist_rhel + bootstrap_containers: + # we need the toolbox container because stock centos has e.g. no + # mount util + x86_64: "quay.io/toolbx-images/centos-toolbox:stream{{.MajorVersion}}" + aarch64: "quay.io/toolbx-images/centos-toolbox:stream{{.MajorVersion}}" - &rhel7 name: "rhel-{{.MajorVersion}}.{{.MinorVersion}}" From 791769f741fb8c6f91ca8d64050dc870195b7101 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 22 Jul 2025 16:36:44 +0200 Subject: [PATCH 14/14] distro: fix bug in `customizations` for kernel options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes a bug in the kernel options handling when getting the kernel options for the image types. Both `osCustomizations` and `ostreeDeploymentCustomizations` need fixing. Instead of the defaultImageConfig we need to get the inherited value from distro default image config and image type image config when looking for the kernel options. Thanks to Tomáš for spotting this! --- pkg/distro/generic/images.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/distro/generic/images.go b/pkg/distro/generic/images.go index a987420ce8..88c7260137 100644 --- a/pkg/distro/generic/images.go +++ b/pkg/distro/generic/images.go @@ -42,11 +42,8 @@ func osCustomizations(t *imageType, osPackageSet rpmmd.PackageSet, options distr osc.KernelName = *imageConfig.DefaultKernelName } - var kernelOptions []string // XXX: keep in sync with the identical copy in rhel/images.go - if t.defaultImageConfig != nil && len(t.defaultImageConfig.KernelOptions) > 0 { - kernelOptions = append(kernelOptions, t.defaultImageConfig.KernelOptions...) - } + kernelOptions := imageConfig.KernelOptions if bpKernel := c.GetKernel(); bpKernel.Append != "" { kernelOptions = append(kernelOptions, bpKernel.Append) } @@ -332,10 +329,7 @@ func ostreeDeploymentCustomizations( imageConfig := t.getDefaultImageConfig() deploymentConf := manifest.OSTreeDeploymentCustomizations{} - var kernelOptions []string - if t.defaultImageConfig != nil && len(t.defaultImageConfig.KernelOptions) > 0 { - kernelOptions = append(kernelOptions, t.defaultImageConfig.KernelOptions...) - } + kernelOptions := imageConfig.KernelOptions if bpKernel := c.GetKernel(); bpKernel != nil && bpKernel.Append != "" { kernelOptions = append(kernelOptions, bpKernel.Append) }