diff --git a/pkg/distro/rhel/images.go b/pkg/distro/rhel/images.go index b61e25cbc8..98f7bc7c38 100644 --- a/pkg/distro/rhel/images.go +++ b/pkg/distro/rhel/images.go @@ -134,6 +134,7 @@ func osCustomizations( // Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true` if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux { osc.SElinux = "targeted" + osc.SELinuxForceRelabel = imageConfig.SELinuxForceRelabel } if t.IsRHEL() && options.Facts != nil { @@ -272,6 +273,7 @@ func osCustomizations( osc.Sysctld = imageConfig.Sysctld osc.DNFConfig = imageConfig.DNFConfig osc.DNFAutomaticConfig = imageConfig.DNFAutomaticConfig + osc.YUMConfig = imageConfig.YumConfig osc.SshdConfig = imageConfig.SshdConfig osc.AuthConfig = imageConfig.Authconfig osc.PwQuality = imageConfig.PwQuality @@ -395,6 +397,18 @@ func DiskImage(workload workload.Workload, img.Filename = t.Filename() + img.VPCForceSize = t.DiskImageVPCForceSize + + if img.OSCustomizations.NoBLS { + img.OSProduct = t.Arch().Distro().Product() + img.OSVersion = t.Arch().Distro().OsVersion() + img.OSNick = t.Arch().Distro().Nick() + } + + if t.DiskImagePartTool != nil { + img.PartTool = *t.DiskImagePartTool + } + return img, nil } diff --git a/pkg/distro/rhel/imagetype.go b/pkg/distro/rhel/imagetype.go index 0141767c4c..a70269ae73 100644 --- a/pkg/distro/rhel/imagetype.go +++ b/pkg/distro/rhel/imagetype.go @@ -15,6 +15,7 @@ import ( "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/image" "github.com/osbuild/images/pkg/manifest" + "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/platform" "github.com/osbuild/images/pkg/rpmmd" ) @@ -84,6 +85,10 @@ type ImageType struct { UnsupportedPartitioningModes []disk.PartitioningMode ISOLabelFn ISOLabelFunc + + // TODO: determine a better place for these options, but for now they are here + DiskImagePartTool *osbuild.PartTool + DiskImageVPCForceSize *bool } func (t *ImageType) Name() string { @@ -305,6 +310,8 @@ func (t *ImageType) Manifest(bp *blueprint.Blueprint, mf := manifest.New() switch t.Arch().Distro().Releasever() { + case "7": + mf.Distro = manifest.DISTRO_EL7 case "8": mf.Distro = manifest.DISTRO_EL8 case "9": diff --git a/pkg/distro/rhel/rhel7/azure.go b/pkg/distro/rhel/rhel7/azure.go index 11a956d651..73f0f0ddae 100644 --- a/pkg/distro/rhel/rhel7/azure.go +++ b/pkg/distro/rhel/rhel7/azure.go @@ -5,31 +5,39 @@ import ( "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/rhel" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/subscription" ) -var azureRhuiImgType = imageType{ - name: "azure-rhui", - filename: "disk.vhd.xz", - mimeType: "application/xz", - compression: "xz", - packageSets: map[string]packageSetFunc{ - osPkgsKey: azureRhuiCommonPackageSet, - }, - packageSetChains: map[string][]string{ - osPkgsKey: {osPkgsKey, blueprintPkgsKey}, - }, - defaultImageConfig: azureDefaultImgConfig, - kernelOptions: "ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y", - bootable: true, - defaultSize: 64 * common.GibiByte, - image: diskImage, - buildPipelines: []string{"build"}, - payloadPipelines: []string{"os", "image", "vpc", "xz"}, - exports: []string{"xz"}, - basePartitionTables: azureRhuiBasePartitionTables, +func mkAzureRhuiImgType() *rhel.ImageType { + it := rhel.NewImageType( + "azure-rhui", + "disk.vhd.xz", + "application/xz", + map[string]rhel.PackageSetFunc{ + rhel.OSPkgsKey: azureRhuiCommonPackageSet, + }, + rhel.DiskImage, + []string{"build"}, + []string{"os", "image", "vpc", "xz"}, + []string{"xz"}, + ) + + // all RHEL 7 images should use sgdisk + it.DiskImagePartTool = common.ToPtr(osbuild.PTSgdisk) + // RHEL 7 qemu vpc subformat does not support force_size + it.DiskImageVPCForceSize = common.ToPtr(false) + + it.Compression = "xz" + it.KernelOptions = "ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y" + it.DefaultImageConfig = azureDefaultImgConfig + it.Bootable = true + it.DefaultSize = 64 * common.GibiByte + it.BasePartitionTables = azureRhuiBasePartitionTables + + return it } var azureDefaultImgConfig = &distro.ImageConfig{ @@ -218,7 +226,7 @@ var azureDefaultImgConfig = &distro.ImageConfig{ DefaultTarget: common.ToPtr("multi-user.target"), } -func azureRhuiCommonPackageSet(t *imageType) rpmmd.PackageSet { +func azureRhuiCommonPackageSet(t *rhel.ImageType) rpmmd.PackageSet { ps := rpmmd.PackageSet{ Include: []string{ "@base", @@ -261,7 +269,7 @@ func azureRhuiCommonPackageSet(t *imageType) rpmmd.PackageSet { }, } - if t.arch.distro.isRHEL() { + if t.IsRHEL() { ps = ps.Append(rpmmd.PackageSet{ Include: []string{ "insights-client", @@ -272,113 +280,119 @@ func azureRhuiCommonPackageSet(t *imageType) rpmmd.PackageSet { return ps } -var azureRhuiBasePartitionTables = distro.BasePartitionTableMap{ - arch.ARCH_X86_64.String(): disk.PartitionTable{ - UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0", - Type: "gpt", - Size: 64 * common.GibiByte, - Partitions: []disk.Partition{ - { - Size: 500 * common.MebiByte, - Type: disk.EFISystemPartitionGUID, - UUID: disk.EFISystemPartitionUUID, - Payload: &disk.Filesystem{ - Type: "vfat", - UUID: disk.EFIFilesystemUUID, - Mountpoint: "/boot/efi", - FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt", - FSTabFreq: 0, - FSTabPassNo: 2, +func azureRhuiBasePartitionTables(t *rhel.ImageType) (disk.PartitionTable, bool) { + switch t.Arch().Name() { + case arch.ARCH_X86_64.String(): + return disk.PartitionTable{ + UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0", + Type: "gpt", + Size: 64 * common.GibiByte, + Partitions: []disk.Partition{ + { + Size: 500 * common.MebiByte, + Type: disk.EFISystemPartitionGUID, + UUID: disk.EFISystemPartitionUUID, + Payload: &disk.Filesystem{ + Type: "vfat", + UUID: disk.EFIFilesystemUUID, + Mountpoint: "/boot/efi", + FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt", + FSTabFreq: 0, + FSTabPassNo: 2, + }, }, - }, - { - Size: 500 * common.MebiByte, - Type: disk.FilesystemDataGUID, - UUID: disk.FilesystemDataUUID, - Payload: &disk.Filesystem{ - Type: "xfs", - Mountpoint: "/boot", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 500 * common.MebiByte, + Type: disk.FilesystemDataGUID, + UUID: disk.FilesystemDataUUID, + Payload: &disk.Filesystem{ + Type: "xfs", + Mountpoint: "/boot", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, - }, - { - Size: 2 * common.MebiByte, - Bootable: true, - Type: disk.BIOSBootPartitionGUID, - UUID: disk.BIOSBootPartitionUUID, - }, - { - Type: disk.LVMPartitionGUID, - UUID: disk.RootPartitionUUID, - Payload: &disk.LVMVolumeGroup{ - Name: "rootvg", - Description: "built with lvm2 and osbuild", - LogicalVolumes: []disk.LVMLogicalVolume{ - { - Size: 1 * common.GibiByte, - Name: "homelv", - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "home", - Mountpoint: "/home", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 2 * common.MebiByte, + Bootable: true, + Type: disk.BIOSBootPartitionGUID, + UUID: disk.BIOSBootPartitionUUID, + }, + { + Type: disk.LVMPartitionGUID, + UUID: disk.RootPartitionUUID, + Payload: &disk.LVMVolumeGroup{ + Name: "rootvg", + Description: "built with lvm2 and osbuild", + LogicalVolumes: []disk.LVMLogicalVolume{ + { + Size: 1 * common.GibiByte, + Name: "homelv", + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "home", + Mountpoint: "/home", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, - }, - { - Size: 2 * common.GibiByte, - Name: "rootlv", - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "root", - Mountpoint: "/", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 2 * common.GibiByte, + Name: "rootlv", + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "root", + Mountpoint: "/", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, - }, - { - Size: 2 * common.GibiByte, - Name: "tmplv", - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "tmp", - Mountpoint: "/tmp", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 2 * common.GibiByte, + Name: "tmplv", + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "tmp", + Mountpoint: "/tmp", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, - }, - { - Size: 10 * common.GibiByte, - Name: "usrlv", - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "usr", - Mountpoint: "/usr", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 10 * common.GibiByte, + Name: "usrlv", + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "usr", + Mountpoint: "/usr", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, - }, - { - Size: 10 * common.GibiByte, // firedrill: 8 GB - Name: "varlv", - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "var", - Mountpoint: "/var", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 10 * common.GibiByte, // firedrill: 8 GB + Name: "varlv", + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "var", + Mountpoint: "/var", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, }, }, }, }, - }, - }, + }, true + + default: + return disk.PartitionTable{}, false + } } diff --git a/pkg/distro/rhel/rhel7/distro.go b/pkg/distro/rhel/rhel7/distro.go index 9a6d0178ec..871c154fb5 100644 --- a/pkg/distro/rhel/rhel7/distro.go +++ b/pkg/distro/rhel/rhel7/distro.go @@ -1,250 +1,82 @@ package rhel7 import ( - "errors" "fmt" - "sort" - "strings" "github.com/osbuild/images/internal/common" "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/rhel" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/platform" - "github.com/osbuild/images/pkg/runner" -) - -const ( - // package set names - - // main/common os image package set name - osPkgsKey = "os" - - // blueprint package set name - blueprintPkgsKey = "blueprint" - - // location for saving openscap remediation data - oscapDataDir = "/oscap_data" ) // RHEL-based OS image configuration defaults -var defaultDistroImageConfig = &distro.ImageConfig{ - Timezone: common.ToPtr("America/New_York"), - Locale: common.ToPtr("en_US.UTF-8"), - GPGKeyFiles: []string{ - "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release", - }, - Sysconfig: []*osbuild.SysconfigStageOptions{ - { - Kernel: &osbuild.SysconfigKernelOptions{ - UpdateDefault: true, - DefaultKernel: "kernel", - }, - Network: &osbuild.SysconfigNetworkOptions{ - Networking: true, - NoZeroConf: true, +func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { + return &distro.ImageConfig{ + Timezone: common.ToPtr("America/New_York"), + Locale: common.ToPtr("en_US.UTF-8"), + GPGKeyFiles: []string{ + "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release", + }, + Sysconfig: []*osbuild.SysconfigStageOptions{ + { + Kernel: &osbuild.SysconfigKernelOptions{ + UpdateDefault: true, + DefaultKernel: "kernel", + }, + Network: &osbuild.SysconfigNetworkOptions{ + Networking: true, + NoZeroConf: true, + }, }, }, - }, - KernelOptionsBootloader: common.ToPtr(true), - NoBLS: common.ToPtr(true), // RHEL 7 grub does not support BLS -} - -// --- Distribution --- -type distribution struct { - name string - product string - nick string - osVersion string - releaseVersion string - modulePlatformID string - vendor string - runner runner.Runner - arches map[string]distro.Arch - defaultImageConfig *distro.ImageConfig -} - -func (d *distribution) Name() string { - return d.name -} - -func (d *distribution) Nick() string { - return d.nick -} - -func (d *distribution) Releasever() string { - return d.releaseVersion -} - -func (d *distribution) OsVersion() string { - return d.osVersion -} - -func (d *distribution) Product() string { - return d.product -} - -func (d *distribution) ModulePlatformID() string { - return d.modulePlatformID -} - -func (d *distribution) OSTreeRef() string { - return "" // not supported -} - -func (d *distribution) ListArches() []string { - archNames := make([]string, 0, len(d.arches)) - for name := range d.arches { - archNames = append(archNames, name) - } - sort.Strings(archNames) - return archNames -} - -func (d *distribution) GetArch(name string) (distro.Arch, error) { - arch, exists := d.arches[name] - if !exists { - return nil, errors.New("invalid architecture: " + name) + KernelOptionsBootloader: common.ToPtr(true), + NoBLS: common.ToPtr(true), // RHEL 7 grub does not support BLS } - return arch, nil } -func (d *distribution) addArches(arches ...architecture) { - if d.arches == nil { - d.arches = map[string]distro.Arch{} - } - - // Do not make copies of architectures, as opposed to image types, - // because architecture definitions are not used by more than a single - // distro definition. - for idx := range arches { - d.arches[arches[idx].name] = &arches[idx] - } -} - -func (d *distribution) isRHEL() bool { - return strings.HasPrefix(d.name, "rhel") -} - -func (d *distribution) getDefaultImageConfig() *distro.ImageConfig { - return d.defaultImageConfig -} - -// --- Architecture --- - -type architecture struct { - distro *distribution - name string - imageTypes map[string]distro.ImageType - imageTypeAliases map[string]string -} - -func (a *architecture) Name() string { - return a.name -} - -func (a *architecture) ListImageTypes() []string { - itNames := make([]string, 0, len(a.imageTypes)) - for name := range a.imageTypes { - itNames = append(itNames, name) - } - sort.Strings(itNames) - return itNames -} - -func (a *architecture) GetImageType(name string) (distro.ImageType, error) { - t, exists := a.imageTypes[name] - if !exists { - aliasForName, exists := a.imageTypeAliases[name] - if !exists { - return nil, errors.New("invalid image type: " + name) - } - t, exists = a.imageTypes[aliasForName] - if !exists { - panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName)) - } - } - return t, nil -} - -func (a *architecture) addImageTypes(platform platform.Platform, imageTypes ...imageType) { - if a.imageTypes == nil { - a.imageTypes = map[string]distro.ImageType{} - } - for idx := range imageTypes { - it := imageTypes[idx] - it.arch = a - it.platform = platform - a.imageTypes[it.name] = &it - for _, alias := range it.nameAliases { - if a.imageTypeAliases == nil { - a.imageTypeAliases = map[string]string{} - } - if existingAliasFor, exists := a.imageTypeAliases[alias]; exists { - panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor)) - } - a.imageTypeAliases[alias] = it.name - } +func newDistro(name string, minor int) *rhel.Distribution { + rd, err := rhel.NewDistribution(name, 7, minor) + if err != nil { + panic(err) } -} - -func (a *architecture) Distro() distro.Distro { - return a.distro -} -func newDistro(name string, minor int) *distribution { - var rd distribution - switch name { - case "rhel": - rd = distribution{ - name: fmt.Sprintf("rhel-7.%d", minor), - product: "Red Hat Enterprise Linux", - osVersion: fmt.Sprintf("7.%d", minor), - nick: "Maipo", - releaseVersion: "7", - modulePlatformID: "platform:el7", - vendor: "redhat", - runner: &runner.RHEL{Major: uint64(7), Minor: uint64(minor)}, - defaultImageConfig: defaultDistroImageConfig, - } - default: - panic(fmt.Sprintf("unknown distro name: %s", name)) - } + rd.CheckOptions = checkOptions + rd.DefaultImageConfig = defaultDistroImageConfig + rd.DistNick = "Maipo" // Architecture definitions - x86_64 := architecture{ - name: arch.ARCH_X86_64.String(), - distro: &rd, - } + x86_64 := rhel.NewArchitecture(rd, arch.ARCH_X86_64) - x86_64.addImageTypes( + x86_64.AddImageTypes( &platform.X86{ BIOS: true, - UEFIVendor: rd.vendor, + UEFIVendor: rd.Vendor(), BasePlatform: platform.BasePlatform{ ImageFormat: platform.FORMAT_QCOW2, QCOW2Compat: "0.10", }, }, - qcow2ImgType, + mkQcow2ImgType(), ) - x86_64.addImageTypes( + x86_64.AddImageTypes( &platform.X86{ BIOS: true, - UEFIVendor: rd.vendor, + UEFIVendor: rd.Vendor(), BasePlatform: platform.BasePlatform{ ImageFormat: platform.FORMAT_VHD, }, }, - azureRhuiImgType, + mkAzureRhuiImgType(), ) - rd.addArches( + rd.AddArches( x86_64, ) - return &rd + return rd } func ParseID(idStr string) (*distro.ID, error) { diff --git a/pkg/distro/rhel/rhel7/images.go b/pkg/distro/rhel/rhel7/images.go deleted file mode 100644 index c6991bf290..0000000000 --- a/pkg/distro/rhel/rhel7/images.go +++ /dev/null @@ -1,272 +0,0 @@ -package rhel7 - -import ( - "fmt" - "math/rand" - - "github.com/osbuild/images/internal/common" - "github.com/osbuild/images/internal/workload" - "github.com/osbuild/images/pkg/blueprint" - "github.com/osbuild/images/pkg/container" - "github.com/osbuild/images/pkg/customizations/users" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/image" - "github.com/osbuild/images/pkg/manifest" - "github.com/osbuild/images/pkg/osbuild" - "github.com/osbuild/images/pkg/rpmmd" -) - -func osCustomizations( - t *imageType, - osPackageSet rpmmd.PackageSet, - options distro.ImageOptions, - containers []container.SourceSpec, - c *blueprint.Customizations, -) (manifest.OSCustomizations, error) { - - imageConfig := t.getDefaultImageConfig() - - osc := manifest.OSCustomizations{} - - if t.bootable { - osc.KernelName = c.GetKernel().Name - - var kernelOptions []string - if t.kernelOptions != "" { - kernelOptions = append(kernelOptions, t.kernelOptions) - } - if bpKernel := c.GetKernel(); bpKernel.Append != "" { - kernelOptions = append(kernelOptions, bpKernel.Append) - } - osc.KernelOptionsAppend = kernelOptions - if imageConfig.KernelOptionsBootloader != nil { - osc.KernelOptionsBootloader = *imageConfig.KernelOptionsBootloader - } - } - - osc.ExtraBasePackages = osPackageSet.Include - osc.ExcludeBasePackages = osPackageSet.Exclude - osc.ExtraBaseRepos = osPackageSet.Repositories - - osc.Containers = containers - - osc.GPGKeyFiles = imageConfig.GPGKeyFiles - if imageConfig.ExcludeDocs != nil { - osc.ExcludeDocs = *imageConfig.ExcludeDocs - } - - // don't put users and groups in the payload of an installer - // add them via kickstart instead - osc.Groups = users.GroupsFromBP(c.GetGroups()) - osc.Users = users.UsersFromBP(c.GetUsers()) - - osc.EnabledServices = imageConfig.EnabledServices - osc.DisabledServices = imageConfig.DisabledServices - osc.MaskedServices = imageConfig.MaskedServices - if imageConfig.DefaultTarget != nil { - osc.DefaultTarget = *imageConfig.DefaultTarget - } - - osc.Firewall = imageConfig.Firewall - if fw := c.GetFirewall(); fw != nil { - options := osbuild.FirewallStageOptions{ - Ports: fw.Ports, - } - - if fw.Services != nil { - options.EnabledServices = fw.Services.Enabled - options.DisabledServices = fw.Services.Disabled - } - if fw.Zones != nil { - for _, z := range fw.Zones { - options.Zones = append(options.Zones, osbuild.FirewallZone{ - Name: *z.Name, - Sources: z.Sources, - }) - } - } - osc.Firewall = &options - } - - language, keyboard := c.GetPrimaryLocale() - if language != nil { - osc.Language = *language - } else if imageConfig.Locale != nil { - osc.Language = *imageConfig.Locale - } - if keyboard != nil { - osc.Keyboard = keyboard - } else if imageConfig.Keyboard != nil { - osc.Keyboard = &imageConfig.Keyboard.Keymap - if imageConfig.Keyboard.X11Keymap != nil { - osc.X11KeymapLayouts = imageConfig.Keyboard.X11Keymap.Layouts - } - } - - if hostname := c.GetHostname(); hostname != nil { - osc.Hostname = *hostname - } - - timezone, ntpServers := c.GetTimezoneSettings() - if timezone != nil { - osc.Timezone = *timezone - } else if imageConfig.Timezone != nil { - osc.Timezone = *imageConfig.Timezone - } - - if len(ntpServers) > 0 { - for _, server := range ntpServers { - osc.NTPServers = append(osc.NTPServers, osbuild.ChronyConfigServer{Hostname: server}) - } - } else if imageConfig.TimeSynchronization != nil { - osc.NTPServers = imageConfig.TimeSynchronization.Servers - osc.LeapSecTZ = imageConfig.TimeSynchronization.LeapsecTz - } - - // Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true` - if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux { - osc.SElinux = "targeted" - osc.SELinuxForceRelabel = imageConfig.SELinuxForceRelabel - } - - if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil { - var datastream = oscapConfig.DataStream - if datastream == "" { - if imageConfig.DefaultOSCAPDatastream == nil { - return manifest.OSCustomizations{}, fmt.Errorf("No OSCAP datastream specified and the distro does not have any default set") - } - datastream = *imageConfig.DefaultOSCAPDatastream - } - - osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions( - oscapDataDir, - osbuild.OscapConfig{ - Datastream: datastream, - ProfileID: oscapConfig.ProfileID, - Compression: true, - }, - ) - } - - if t.arch.distro.isRHEL() && options.Facts != nil { - osc.FactAPIType = &options.Facts.APIType - } - - var err error - osc.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(c.GetDirectories()) - if err != nil { - // In theory this should never happen, because the blueprint directory customizations - // should have been validated before this point. - panic(fmt.Sprintf("failed to convert directory customizations to fs node directories: %v", err)) - } - - osc.Files, err = blueprint.FileCustomizationsToFsNodeFiles(c.GetFiles()) - if err != nil { - // In theory this should never happen, because the blueprint file customizations - // should have been validated before this point. - panic(fmt.Sprintf("failed to convert file customizations to fs node files: %v", err)) - } - - // set yum repos first, so it doesn't get overridden by - // imageConfig.YUMRepos - osc.YUMRepos = imageConfig.YUMRepos - - customRepos, err := c.GetRepositories() - if err != nil { - // This shouldn't happen and since the repos - // should have already been validated - panic(fmt.Sprintf("failed to get custom repos: %v", err)) - } - - // This function returns a map of filename and corresponding yum repos - // and a list of fs node files for the inline gpg keys so we can save - // them to disk. This step also swaps the inline gpg key with the path - // to the file in the os file tree - yumRepos, gpgKeyFiles, err := blueprint.RepoCustomizationsToRepoConfigAndGPGKeyFiles(customRepos) - if err != nil { - panic(fmt.Sprintf("failed to convert inline gpgkeys to fs node files: %v", err)) - } - - // add the gpg key files to the list of files to be added to the tree - if len(gpgKeyFiles) > 0 { - osc.Files = append(osc.Files, gpgKeyFiles...) - } - - for filename, repos := range yumRepos { - osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos)) - } - - osc.ShellInit = imageConfig.ShellInit - - osc.Grub2Config = imageConfig.Grub2Config - osc.Sysconfig = imageConfig.Sysconfig - osc.SystemdLogind = imageConfig.SystemdLogind - osc.CloudInit = imageConfig.CloudInit - osc.Modprobe = imageConfig.Modprobe - osc.DracutConf = imageConfig.DracutConf - osc.SystemdUnit = imageConfig.SystemdUnit - osc.Authselect = imageConfig.Authselect - osc.SELinuxConfig = imageConfig.SELinuxConfig - osc.Tuned = imageConfig.Tuned - osc.Tmpfilesd = imageConfig.Tmpfilesd - osc.PamLimitsConf = imageConfig.PamLimitsConf - osc.Sysctld = imageConfig.Sysctld - osc.DNFConfig = imageConfig.DNFConfig - osc.DNFAutomaticConfig = imageConfig.DNFAutomaticConfig - osc.YUMConfig = imageConfig.YumConfig - osc.SshdConfig = imageConfig.SshdConfig - osc.AuthConfig = imageConfig.Authconfig - osc.PwQuality = imageConfig.PwQuality - osc.RHSMConfig = imageConfig.RHSMConfig - osc.Subscription = options.Subscription - osc.WAAgentConfig = imageConfig.WAAgentConfig - osc.UdevRules = imageConfig.UdevRules - osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig - - osc.Files = append(osc.Files, imageConfig.Files...) - osc.Directories = append(osc.Directories, imageConfig.Directories...) - - if imageConfig.NoBLS != nil { - osc.NoBLS = *imageConfig.NoBLS - } - - return osc, nil -} - -func diskImage(workload workload.Workload, - t *imageType, - customizations *blueprint.Customizations, - options distro.ImageOptions, - packageSets map[string]rpmmd.PackageSet, - containers []container.SourceSpec, - rng *rand.Rand) (image.ImageKind, error) { - - img := image.NewDiskImage() - img.Platform = t.platform - - var err error - img.OSCustomizations, err = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations) - if err != nil { - return nil, err - } - - img.Environment = t.environment - img.Workload = workload - img.Compression = t.compression - img.PartTool = osbuild.PTSgdisk // all RHEL 7 images should use sgdisk - img.VPCForceSize = common.ToPtr(false) // RHEL 7 qemu vpc subformat does not support force_size - img.OSProduct = t.arch.distro.product - img.OSVersion = t.arch.distro.osVersion - img.OSNick = t.arch.distro.nick - - // TODO: move generation into LiveImage - pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) - if err != nil { - return nil, err - } - img.PartitionTable = pt - - img.Filename = t.Filename() - - return img, nil -} diff --git a/pkg/distro/rhel/rhel7/imagetype.go b/pkg/distro/rhel/rhel7/imagetype.go deleted file mode 100644 index 4a6f4020fa..0000000000 --- a/pkg/distro/rhel/rhel7/imagetype.go +++ /dev/null @@ -1,310 +0,0 @@ -package rhel7 - -import ( - "fmt" - "math/rand" - - "github.com/osbuild/images/internal/environment" - "github.com/osbuild/images/internal/workload" - "github.com/osbuild/images/pkg/blueprint" - "github.com/osbuild/images/pkg/container" - "github.com/osbuild/images/pkg/disk" - "github.com/osbuild/images/pkg/distro" - "github.com/osbuild/images/pkg/image" - "github.com/osbuild/images/pkg/manifest" - "github.com/osbuild/images/pkg/platform" - "github.com/osbuild/images/pkg/policies" - "github.com/osbuild/images/pkg/rpmmd" - "golang.org/x/exp/slices" -) - -type packageSetFunc func(t *imageType) rpmmd.PackageSet - -type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error) - -type isoLabelFunc func(t *imageType) string - -type imageType struct { - arch *architecture - platform platform.Platform - environment environment.Environment - workload workload.Workload - name string - nameAliases []string - filename string - compression string // TODO: remove from image definition and make it a transport option - mimeType string - packageSets map[string]packageSetFunc - packageSetChains map[string][]string - defaultImageConfig *distro.ImageConfig - kernelOptions string - defaultSize uint64 - buildPipelines []string - payloadPipelines []string - exports []string - image imageFunc - isoLabel isoLabelFunc - - // bootISO: installable ISO - bootISO bool - // bootable image - bootable bool - // List of valid arches for the image type - basePartitionTables distro.BasePartitionTableMap -} - -func (t *imageType) Name() string { - return t.name -} - -func (t *imageType) Arch() distro.Arch { - return t.arch -} - -func (t *imageType) Filename() string { - return t.filename -} - -func (t *imageType) MIMEType() string { - return t.mimeType -} - -func (t *imageType) OSTreeRef() string { - // Not supported - return "" -} - -func (t *imageType) ISOLabel() (string, error) { - if !t.bootISO { - return "", fmt.Errorf("image type %q is not an ISO", t.name) - } - - if t.isoLabel != nil { - return t.isoLabel(t), nil - } - - return "", nil -} - -func (t *imageType) Size(size uint64) uint64 { - if size == 0 { - size = t.defaultSize - } - return size -} - -func (t *imageType) BuildPipelines() []string { - return t.buildPipelines -} - -func (t *imageType) PayloadPipelines() []string { - return t.payloadPipelines -} - -func (t *imageType) PayloadPackageSets() []string { - return []string{blueprintPkgsKey} -} - -func (t *imageType) PackageSetsChains() map[string][]string { - return t.packageSetChains -} - -func (t *imageType) Exports() []string { - if len(t.exports) == 0 { - panic(fmt.Sprintf("programming error: no exports for '%s'", t.name)) - } - return t.exports -} - -func (t *imageType) BootMode() distro.BootMode { - if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" { - return distro.BOOT_HYBRID - } else if t.platform.GetUEFIVendor() != "" { - return distro.BOOT_UEFI - } else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() { - return distro.BOOT_LEGACY - } - return distro.BOOT_NONE -} - -func (t *imageType) getPartitionTable( - mountpoints []blueprint.FilesystemCustomization, - options distro.ImageOptions, - rng *rand.Rand, -) (*disk.PartitionTable, error) { - archName := t.arch.Name() - - basePartitionTable, exists := t.basePartitionTables[archName] - - if !exists { - return nil, fmt.Errorf("unknown arch: " + archName) - } - - imageSize := t.Size(options.Size) - - return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, options.PartitioningMode, nil, rng) -} - -func (t *imageType) getDefaultImageConfig() *distro.ImageConfig { - // ensure that image always returns non-nil default config - imageConfig := t.defaultImageConfig - if imageConfig == nil { - imageConfig = &distro.ImageConfig{} - } - return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig()) - -} - -func (t *imageType) PartitionType() string { - archName := t.arch.Name() - basePartitionTable, exists := t.basePartitionTables[archName] - if !exists { - return "" - } - - return basePartitionTable.Type -} - -func (t *imageType) Manifest(bp *blueprint.Blueprint, - options distro.ImageOptions, - repos []rpmmd.RepoConfig, - seed int64) (*manifest.Manifest, []string, error) { - - warnings, err := t.checkOptions(bp, options) - if err != nil { - return nil, nil, err - } - - // merge package sets that appear in the image type with the package sets - // of the same name from the distro and arch - staticPackageSets := make(map[string]rpmmd.PackageSet) - - for name, getter := range t.packageSets { - staticPackageSets[name] = getter(t) - } - - // amend with repository information and collect payload repos - payloadRepos := make([]rpmmd.RepoConfig, 0) - for _, repo := range repos { - if len(repo.PackageSets) > 0 { - // only apply the repo to the listed package sets - for _, psName := range repo.PackageSets { - if slices.Contains(t.PayloadPackageSets(), psName) { - payloadRepos = append(payloadRepos, repo) - } - ps := staticPackageSets[psName] - ps.Repositories = append(ps.Repositories, repo) - staticPackageSets[psName] = ps - } - } - } - - w := t.workload - if w == nil { - cw := &workload.Custom{ - BaseWorkload: workload.BaseWorkload{ - Repos: payloadRepos, - }, - Packages: bp.GetPackagesEx(false), - } - if services := bp.Customizations.GetServices(); services != nil { - cw.Services = services.Enabled - cw.DisabledServices = services.Disabled - } - w = cw - } - - containerSources := make([]container.SourceSpec, len(bp.Containers)) - for idx, cont := range bp.Containers { - containerSources[idx] = container.SourceSpec{ - Source: cont.Source, - Name: cont.Name, - TLSVerify: cont.TLSVerify, - Local: cont.LocalStorage, - } - } - - source := rand.NewSource(seed) - // math/rand is good enough in this case - /* #nosec G404 */ - rng := rand.New(source) - - if t.image == nil { - return nil, nil, nil - } - img, err := t.image(w, t, bp.Customizations, options, staticPackageSets, containerSources, rng) - if err != nil { - return nil, nil, err - } - mf := manifest.New() - mf.Distro = manifest.DISTRO_EL7 - _, err = img.InstantiateManifest(&mf, repos, t.arch.distro.runner, rng) - if err != nil { - return nil, nil, err - } - - return &mf, warnings, err -} - -// 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 (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) { - customizations := bp.Customizations - // holds warnings (e.g. deprecation notices) - var warnings []string - if t.workload != nil { - // For now, if an image type defines its own workload, don't allow any - // user customizations. - // Soon we will have more workflows and each will define its allowed - // set of customizations. The current set of customizations defined in - // the blueprint spec corresponds to the Custom workflow. - if customizations != nil { - return warnings, fmt.Errorf(distro.NoCustomizationsAllowedError, t.name) - } - } - - if len(bp.Containers) > 0 { - return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name) - } - - mountpoints := customizations.GetFilesystems() - - err := blueprint.CheckMountpointsPolicy(mountpoints, policies.MountpointPolicies) - if err != nil { - return warnings, err - } - - if osc := customizations.GetOpenSCAP(); osc != nil { - return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion)) - } - - // 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 - - 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 - } - - return warnings, nil -} diff --git a/pkg/distro/rhel/rhel7/options.go b/pkg/distro/rhel/rhel7/options.go new file mode 100644 index 0000000000..181f43e3d3 --- /dev/null +++ b/pkg/distro/rhel/rhel7/options.go @@ -0,0 +1,63 @@ +package rhel7 + +import ( + "fmt" + + "github.com/osbuild/images/pkg/blueprint" + "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 + + if len(bp.Containers) > 0 { + return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.Name(), t.Arch().Distro().Name()) + } + + mountpoints := customizations.GetFilesystems() + + err := blueprint.CheckMountpointsPolicy(mountpoints, policies.MountpointPolicies) + if err != nil { + return warnings, err + } + + if osc := customizations.GetOpenSCAP(); osc != nil { + return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.Arch().Distro().OsVersion())) + } + + // 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 + + 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 + } + + return warnings, nil +} diff --git a/pkg/distro/rhel/rhel7/package_sets.go b/pkg/distro/rhel/rhel7/package_sets.go index d888edb8e1..37248dcc0f 100644 --- a/pkg/distro/rhel/rhel7/package_sets.go +++ b/pkg/distro/rhel/rhel7/package_sets.go @@ -1,12 +1,13 @@ package rhel7 import ( + "github.com/osbuild/images/pkg/distro/rhel" "github.com/osbuild/images/pkg/rpmmd" ) // packages that are only in some (sub)-distributions -func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet { - if t.arch.distro.isRHEL() { +func distroSpecificPackageSet(t *rhel.ImageType) rpmmd.PackageSet { + if t.IsRHEL() { return rpmmd.PackageSet{ Include: []string{"insights-client"}, } diff --git a/pkg/distro/rhel/rhel7/partition_tables.go b/pkg/distro/rhel/rhel7/partition_tables.go index f0f046d4c6..b2ad9851c2 100644 --- a/pkg/distro/rhel/rhel7/partition_tables.go +++ b/pkg/distro/rhel/rhel7/partition_tables.go @@ -4,60 +4,66 @@ import ( "github.com/osbuild/images/internal/common" "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/disk" - "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/rhel" ) -var defaultBasePartitionTables = distro.BasePartitionTableMap{ - arch.ARCH_X86_64.String(): disk.PartitionTable{ - UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0", - Type: "gpt", - Partitions: []disk.Partition{ - { - Size: 1 * common.MebiByte, - Bootable: true, - Type: disk.BIOSBootPartitionGUID, - UUID: disk.BIOSBootPartitionUUID, - }, - { - Size: 200 * common.MebiByte, - Type: disk.EFISystemPartitionGUID, - UUID: disk.EFISystemPartitionUUID, - Payload: &disk.Filesystem{ - Type: "vfat", - UUID: disk.EFIFilesystemUUID, - Mountpoint: "/boot/efi", - Label: "EFI-SYSTEM", - FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt", - FSTabFreq: 0, - FSTabPassNo: 2, +func defaultBasePartitionTables(t *rhel.ImageType) (disk.PartitionTable, bool) { + switch t.Arch().Name() { + case arch.ARCH_X86_64.String(): + return disk.PartitionTable{ + UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0", + Type: "gpt", + Partitions: []disk.Partition{ + { + Size: 1 * common.MebiByte, + Bootable: true, + Type: disk.BIOSBootPartitionGUID, + UUID: disk.BIOSBootPartitionUUID, }, - }, - { - Size: 500 * common.MebiByte, - Type: disk.FilesystemDataGUID, - UUID: disk.FilesystemDataUUID, - Payload: &disk.Filesystem{ - Type: "xfs", - Mountpoint: "/boot", - Label: "boot", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 200 * common.MebiByte, + Type: disk.EFISystemPartitionGUID, + UUID: disk.EFISystemPartitionUUID, + Payload: &disk.Filesystem{ + Type: "vfat", + UUID: disk.EFIFilesystemUUID, + Mountpoint: "/boot/efi", + Label: "EFI-SYSTEM", + FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt", + FSTabFreq: 0, + FSTabPassNo: 2, + }, }, - }, - { - Size: 2 * common.GibiByte, - Type: disk.FilesystemDataGUID, - UUID: disk.RootPartitionUUID, - Payload: &disk.Filesystem{ - Type: "xfs", - Label: "root", - Mountpoint: "/", - FSTabOptions: "defaults", - FSTabFreq: 0, - FSTabPassNo: 0, + { + Size: 500 * common.MebiByte, + Type: disk.FilesystemDataGUID, + UUID: disk.FilesystemDataUUID, + Payload: &disk.Filesystem{ + Type: "xfs", + Mountpoint: "/boot", + Label: "boot", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, + }, + { + Size: 2 * common.GibiByte, + Type: disk.FilesystemDataGUID, + UUID: disk.RootPartitionUUID, + Payload: &disk.Filesystem{ + Type: "xfs", + Label: "root", + Mountpoint: "/", + FSTabOptions: "defaults", + FSTabFreq: 0, + FSTabPassNo: 0, + }, }, }, - }, - }, + }, true + + default: + return disk.PartitionTable{}, false + } } diff --git a/pkg/distro/rhel/rhel7/qcow2.go b/pkg/distro/rhel/rhel7/qcow2.go index a875fadd76..04f1e911c2 100644 --- a/pkg/distro/rhel/rhel7/qcow2.go +++ b/pkg/distro/rhel/rhel7/qcow2.go @@ -3,27 +3,36 @@ package rhel7 import ( "github.com/osbuild/images/internal/common" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/images/pkg/distro/rhel" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/subscription" ) -var qcow2ImgType = imageType{ - name: "qcow2", - filename: "disk.qcow2", - mimeType: "application/x-qemu-disk", - kernelOptions: "console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=auto", - packageSets: map[string]packageSetFunc{ - osPkgsKey: qcow2CommonPackageSet, - }, - defaultImageConfig: qcow2DefaultImgConfig, - bootable: true, - defaultSize: 10 * common.GibiByte, - image: diskImage, - buildPipelines: []string{"build"}, - payloadPipelines: []string{"os", "image", "qcow2"}, - exports: []string{"qcow2"}, - basePartitionTables: defaultBasePartitionTables, +func mkQcow2ImgType() *rhel.ImageType { + it := rhel.NewImageType( + "qcow2", + "disk.qcow2", + "application/x-qemu-disk", + map[string]rhel.PackageSetFunc{ + rhel.OSPkgsKey: qcow2CommonPackageSet, + }, + rhel.DiskImage, + []string{"build"}, + []string{"os", "image", "qcow2"}, + []string{"qcow2"}, + ) + + // all RHEL 7 images should use sgdisk + it.DiskImagePartTool = common.ToPtr(osbuild.PTSgdisk) + + it.KernelOptions = "console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=auto" + it.Bootable = true + it.DefaultSize = 10 * common.GibiByte + it.DefaultImageConfig = qcow2DefaultImgConfig + it.BasePartitionTables = defaultBasePartitionTables + + return it } var qcow2DefaultImgConfig = &distro.ImageConfig{ @@ -68,7 +77,7 @@ var qcow2DefaultImgConfig = &distro.ImageConfig{ }, } -func qcow2CommonPackageSet(t *imageType) rpmmd.PackageSet { +func qcow2CommonPackageSet(t *rhel.ImageType) rpmmd.PackageSet { ps := rpmmd.PackageSet{ Include: []string{ "@core",