diff --git a/cmd/gen-manifests/main.go b/cmd/gen-manifests/main.go index a09bd8639..34cd72253 100644 --- a/cmd/gen-manifests/main.go +++ b/cmd/gen-manifests/main.go @@ -73,6 +73,7 @@ type crBlueprint struct { Version string `json:"version,omitempty"` Packages []blueprint.Package `json:"packages,omitempty"` Modules []blueprint.Package `json:"modules,omitempty"` + EnabledModules []blueprint.EnabledModule `json:"enabled_modules" toml:"enabled_modules"` Groups []blueprint.Group `json:"groups,omitempty"` Containers []blueprint.Container `json:"containers,omitempty"` Customizations *blueprint.Customizations `json:"customizations,omitempty"` diff --git a/go.mod b/go.mod index a3e7dd636..dc6eb34e4 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/labstack/gommon v0.4.2 github.com/openshift-online/ocm-sdk-go v0.1.438 github.com/oracle/oci-go-sdk/v54 v54.0.0 - github.com/osbuild/images v0.120.0 + github.com/osbuild/images v0.123.0 github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d github.com/osbuild/pulp-client v0.1.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 4f0dc990f..685fbdece 100644 --- a/go.sum +++ b/go.sum @@ -547,8 +547,8 @@ github.com/openshift-online/ocm-sdk-go v0.1.438 h1:tsLCCUzbLCTL4RZG02y9RuopmGCXp github.com/openshift-online/ocm-sdk-go v0.1.438/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y= github.com/oracle/oci-go-sdk/v54 v54.0.0 h1:CDLjeSejv2aDpElAJrhKpi6zvT/zhZCZuXchUUZ+LS4= github.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc= -github.com/osbuild/images v0.120.0 h1:6zXCp59AG03qajZlg/GJ07Fr4E6z5qaZshOuWgAse7g= -github.com/osbuild/images v0.120.0/go.mod h1:Ag87vmyxooiPQBJEDILbypG8/SRIear75YA78NwLix0= +github.com/osbuild/images v0.123.0 h1:9b2sfl6751dpAEU3wR0bMN1d/bEhbJ39N5a/9ZVCxcg= +github.com/osbuild/images v0.123.0/go.mod h1:Ag87vmyxooiPQBJEDILbypG8/SRIear75YA78NwLix0= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d h1:r9BFPDv0uuA9k1947Jybcxs36c/pTywWS1gjeizvtcQ= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d/go.mod h1:zR1iu/hOuf+OQNJlk70tju9IqzzM4ycq0ectkFBm94U= github.com/osbuild/pulp-client v0.1.0 h1:L0C4ezBJGTamN3BKdv+rKLuq/WxXJbsFwz/Hj7aEmJ8= diff --git a/internal/blueprint/repository_customizations.go b/internal/blueprint/repository_customizations.go index fb72bfd53..e2e5adb75 100644 --- a/internal/blueprint/repository_customizations.go +++ b/internal/blueprint/repository_customizations.go @@ -21,6 +21,10 @@ type RepositoryCustomization struct { SSLVerify *bool `json:"sslverify,omitempty" toml:"sslverify,omitempty"` ModuleHotfixes *bool `json:"module_hotfixes,omitempty" toml:"module_hotfixes,omitempty"` Filename string `json:"filename,omitempty" toml:"filename,omitempty"` + + // When set the repository will be used during the depsolve of + // payload repositories to install packages from it. + InstallFrom bool `json:"install_from" toml:"install_from"` } const repoFilenameRegex = "^[\\w.-]{1,250}\\.repo$" diff --git a/vendor/github.com/osbuild/images/data/dependencies/deps.go b/vendor/github.com/osbuild/images/data/dependencies/deps.go new file mode 100644 index 000000000..1a1063dd5 --- /dev/null +++ b/vendor/github.com/osbuild/images/data/dependencies/deps.go @@ -0,0 +1,14 @@ +package dependencies + +import ( + _ "embed" +) + +//go:embed osbuild +var minimumOSBuildVersion string + +// MinimumOSBuildVersion returns the minimum version of osbuild required by this +// version of images module. +func MinimumOSBuildVersion() string { + return minimumOSBuildVersion +} diff --git a/vendor/github.com/osbuild/images/data/dependencies/osbuild b/vendor/github.com/osbuild/images/data/dependencies/osbuild new file mode 100644 index 000000000..b5db9c417 --- /dev/null +++ b/vendor/github.com/osbuild/images/data/dependencies/osbuild @@ -0,0 +1 @@ +139 \ No newline at end of file diff --git a/vendor/github.com/osbuild/images/internal/workload/custom.go b/vendor/github.com/osbuild/images/internal/workload/custom.go index 47f5d3ee3..ffda0e683 100644 --- a/vendor/github.com/osbuild/images/internal/workload/custom.go +++ b/vendor/github.com/osbuild/images/internal/workload/custom.go @@ -3,6 +3,7 @@ package workload type Custom struct { BaseWorkload Packages []string + EnabledModules []string Services []string DisabledServices []string } @@ -11,6 +12,10 @@ func (p *Custom) GetPackages() []string { return p.Packages } +func (p *Custom) GetEnabledModules() []string { + return p.EnabledModules +} + func (p *Custom) GetServices() []string { return p.Services } diff --git a/vendor/github.com/osbuild/images/internal/workload/workload.go b/vendor/github.com/osbuild/images/internal/workload/workload.go index 24aa570f0..64c307186 100644 --- a/vendor/github.com/osbuild/images/internal/workload/workload.go +++ b/vendor/github.com/osbuild/images/internal/workload/workload.go @@ -4,6 +4,7 @@ import "github.com/osbuild/images/pkg/rpmmd" type Workload interface { GetPackages() []string + GetEnabledModules() []string GetRepos() []rpmmd.RepoConfig GetServices() []string GetDisabledServices() []string @@ -17,6 +18,10 @@ func (p BaseWorkload) GetPackages() []string { return []string{} } +func (p BaseWorkload) GetEnabledModules() []string { + return []string{} +} + func (p BaseWorkload) GetRepos() []rpmmd.RepoConfig { return p.Repos } diff --git a/vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go b/vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go index 6fdf205f6..daaf8d99f 100644 --- a/vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go +++ b/vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go @@ -3,11 +3,16 @@ package blueprint // A Blueprint is a high-level description of an image. type Blueprint struct { - Name string `json:"name" toml:"name"` - Description string `json:"description" toml:"description"` - Version string `json:"version,omitempty" toml:"version,omitempty"` - Packages []Package `json:"packages" toml:"packages"` - Modules []Package `json:"modules" toml:"modules"` + Name string `json:"name" toml:"name"` + Description string `json:"description" toml:"description"` + Version string `json:"version,omitempty" toml:"version,omitempty"` + Packages []Package `json:"packages" toml:"packages"` + Modules []Package `json:"modules" toml:"modules"` + + // Note, this is called "enabled modules" because we already have "modules" except + // the "modules" refers to packages and "enabled modules" refers to modularity modules. + EnabledModules []EnabledModule `json:"enabled_modules" toml:"enabled_modules"` + Groups []Group `json:"groups" toml:"groups"` Containers []Container `json:"containers,omitempty" toml:"containers,omitempty"` Customizations *Customizations `json:"customizations,omitempty" toml:"customizations"` @@ -23,6 +28,12 @@ type Package struct { Version string `json:"version,omitempty" toml:"version,omitempty"` } +// A module specifies a modularity stream. +type EnabledModule struct { + Name string `json:"name" toml:"name"` + Stream string `json:"stream,omitempty" toml:"stream,omitempty"` +} + // A group specifies an package group. type Group struct { Name string `json:"name" toml:"name"` @@ -63,6 +74,16 @@ func (b *Blueprint) GetPackagesEx(bootable bool) []string { return packages } +func (b *Blueprint) GetEnabledModules() []string { + modules := []string{} + + for _, mod := range b.EnabledModules { + modules = append(modules, mod.ToNameStream()) + } + + return modules +} + func (p Package) ToNameVersion() string { // Omit version to prevent all packages with prefix of name to be installed if p.Version == "*" || p.Version == "" { @@ -71,3 +92,7 @@ func (p Package) ToNameVersion() string { return p.Name + "-" + p.Version } + +func (p EnabledModule) ToNameStream() string { + return p.Name + ":" + p.Stream +} diff --git a/vendor/github.com/osbuild/images/pkg/blueprint/customizations.go b/vendor/github.com/osbuild/images/pkg/blueprint/customizations.go index bcb1f9899..d4f9b9e1e 100644 --- a/vendor/github.com/osbuild/images/pkg/blueprint/customizations.go +++ b/vendor/github.com/osbuild/images/pkg/blueprint/customizations.go @@ -376,8 +376,8 @@ func (c *Customizations) GetRepositories() ([]RepositoryCustomization, error) { return nil, nil } - for idx := range c.Repositories { - err := validateCustomRepository(&c.Repositories[idx]) + for _, repo := range c.Repositories { + err := validateCustomRepository(&repo) if err != nil { return nil, err } diff --git a/vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go b/vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go index 1159f8d76..958d38d6c 100644 --- a/vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go +++ b/vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go @@ -25,6 +25,10 @@ type RepositoryCustomization struct { SSLVerify *bool `json:"sslverify,omitempty" toml:"sslverify,omitempty"` ModuleHotfixes *bool `json:"module_hotfixes,omitempty" toml:"module_hotfixes,omitempty"` Filename string `json:"filename,omitempty" toml:"filename,omitempty"` + + // When set the repository will be used during the depsolve of + // payload repositories to install packages from it. + InstallFrom bool `json:"install_from" toml:"install_from"` } const repoFilenameRegex = "^[\\w.-]{1,250}\\.repo$" @@ -76,6 +80,17 @@ func (rc *RepositoryCustomization) getFilename() string { return rc.Filename } +func RepoCustomizationsInstallFromOnly(repos []RepositoryCustomization) []rpmmd.RepoConfig { + var res []rpmmd.RepoConfig + for _, repo := range repos { + if !repo.InstallFrom { + continue + } + res = append(res, repo.customRepoToRepoConfig()) + } + return res +} + func RepoCustomizationsToRepoConfigAndGPGKeyFiles(repos []RepositoryCustomization) (map[string][]rpmmd.RepoConfig, []*fsnode.File, error) { if len(repos) == 0 { return nil, nil, nil diff --git a/vendor/github.com/osbuild/images/pkg/distro/fedora/distro.go b/vendor/github.com/osbuild/images/pkg/distro/fedora/distro.go index 13e6d5e3a..982560e55 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/fedora/distro.go +++ b/vendor/github.com/osbuild/images/pkg/distro/fedora/distro.go @@ -57,35 +57,11 @@ var ( "/": 1 * datasizes.GiB, "/usr": 2 * datasizes.GiB, } +) - // Services - iotServices = []string{ - "NetworkManager.service", - "firewalld.service", - "sshd.service", - "zezere_ignition.timer", - "zezere_ignition_banner.service", - "greenboot-grub2-set-counter", - "greenboot-grub2-set-success", - "greenboot-healthcheck", - "greenboot-rpm-ostree-grub2-check-fallback", - "greenboot-status", - "greenboot-task-runner", - "redboot-auto-reboot", - "redboot-task-runner", - "parsec", - "dbus-parsec", - } - - minimalRawServices = []string{ - "NetworkManager.service", - "firewalld.service", - "initial-setup.service", - "sshd.service", - } - - // Image Definitions - imageInstallerImgType = imageType{ +// Image Definitions +func mkImageInstallerImgType(d distribution) imageType { + return imageType{ name: "image-installer", nameAliases: []string{"fedora-image-installer"}, filename: "installer.iso", @@ -93,11 +69,7 @@ var ( packageSets: map[string]packageSetFunc{ osPkgsKey: func(t *imageType) rpmmd.PackageSet { // use the minimal raw image type for the OS package set - ft := &imageType{ - name: "minimal-raw", - arch: t.arch, - } - return packagesets.Load(ft, VersionReplacements()) + return packagesets.Load(t, "minimal-raw", VersionReplacements()) }, installerPkgsKey: packageSetLoader, }, @@ -115,8 +87,10 @@ var ( exports: []string{"bootiso"}, requiredPartitionSizes: requiredDirectorySizes, } +} - liveInstallerImgType = imageType{ +func mkLiveInstallerImgType(d distribution) imageType { + return imageType{ name: "live-installer", nameAliases: []string{}, filename: "live-installer.iso", @@ -137,8 +111,10 @@ var ( exports: []string{"bootiso"}, requiredPartitionSizes: requiredDirectorySizes, } +} - iotCommitImgType = imageType{ +func mkIotCommitImgType(d distribution) imageType { + return imageType{ name: "iot-commit", nameAliases: []string{"fedora-iot-commit"}, filename: "commit.tar", @@ -147,8 +123,9 @@ var ( osPkgsKey: packageSetLoader, }, defaultImageConfig: &distro.ImageConfig{ - EnabledServices: iotServices, - DracutConf: []*osbuild.DracutConfStageOptions{osbuild.FIPSDracutConfStageOptions}, + EnabledServices: iotServicesForVersion(&d), + DracutConf: []*osbuild.DracutConfStageOptions{osbuild.FIPSDracutConfStageOptions}, + MachineIdUninitialized: common.ToPtr(false), }, rpmOstree: true, image: iotCommitImage, @@ -157,14 +134,19 @@ var ( exports: []string{"commit-archive"}, requiredPartitionSizes: requiredDirectorySizes, } +} - iotBootableContainer = imageType{ +func mkIotBootableContainer(d distribution) imageType { + return imageType{ name: "iot-bootable-container", filename: "iot-bootable-container.tar", mimeType: "application/x-tar", packageSets: map[string]packageSetFunc{ osPkgsKey: packageSetLoader, }, + defaultImageConfig: &distro.ImageConfig{ + MachineIdUninitialized: common.ToPtr(false), + }, rpmOstree: true, image: bootableContainerImage, buildPipelines: []string{"build"}, @@ -172,8 +154,10 @@ var ( exports: []string{"ostree-encapsulate"}, requiredPartitionSizes: requiredDirectorySizes, } +} - iotOCIImgType = imageType{ +func mkIotOCIImgType(d distribution) imageType { + return imageType{ name: "iot-container", nameAliases: []string{"fedora-iot-container"}, filename: "container.tar", @@ -185,8 +169,9 @@ var ( }, }, defaultImageConfig: &distro.ImageConfig{ - EnabledServices: iotServices, - DracutConf: []*osbuild.DracutConfStageOptions{osbuild.FIPSDracutConfStageOptions}, + EnabledServices: iotServicesForVersion(&d), + DracutConf: []*osbuild.DracutConfStageOptions{osbuild.FIPSDracutConfStageOptions}, + MachineIdUninitialized: common.ToPtr(false), }, rpmOstree: true, bootISO: false, @@ -196,8 +181,10 @@ var ( exports: []string{"container"}, requiredPartitionSizes: requiredDirectorySizes, } +} - iotInstallerImgType = imageType{ +func mkIotInstallerImgType(d distribution) imageType { + return imageType{ name: "iot-installer", nameAliases: []string{"fedora-iot-installer"}, filename: "installer.iso", @@ -206,8 +193,8 @@ var ( installerPkgsKey: packageSetLoader, }, defaultImageConfig: &distro.ImageConfig{ + EnabledServices: iotServicesForVersion(&d), Locale: common.ToPtr("en_US.UTF-8"), - EnabledServices: iotServices, }, rpmOstree: true, bootISO: true, @@ -218,8 +205,10 @@ var ( exports: []string{"bootiso"}, requiredPartitionSizes: requiredDirectorySizes, } +} - iotSimplifiedInstallerImgType = imageType{ +func mkIotSimplifiedInstallerImgType(d distribution) imageType { + return imageType{ name: "iot-simplified-installer", filename: "simplified-installer.iso", mimeType: "application/x-iso9660-image", @@ -227,7 +216,7 @@ var ( installerPkgsKey: packageSetLoader, }, defaultImageConfig: &distro.ImageConfig{ - EnabledServices: iotServices, + EnabledServices: iotServicesForVersion(&d), Keyboard: &osbuild.KeymapStageOptions{ Keymap: "us", }, @@ -249,8 +238,10 @@ var ( kernelOptions: ostreeDeploymentKernelOptions, requiredPartitionSizes: requiredDirectorySizes, } +} - iotRawImgType = imageType{ +func mkIotRawImgType(d distribution) imageType { + return imageType{ name: "iot-raw-image", nameAliases: []string{"fedora-iot-raw-image"}, filename: "image.raw.xz", @@ -281,8 +272,10 @@ var ( // override them (and make them smaller, in this case). requiredPartitionSizes: map[string]uint64{}, } +} - iotQcow2ImgType = imageType{ +func mkIotQcow2ImgType(d distribution) imageType { + return imageType{ name: "iot-qcow2-image", filename: "image.qcow2", mimeType: "application/x-qemu-disk", @@ -307,8 +300,10 @@ var ( kernelOptions: ostreeDeploymentKernelOptions, requiredPartitionSizes: requiredDirectorySizes, } +} - qcow2ImgType = imageType{ +func mkQcow2ImgType(d distribution) imageType { + return imageType{ name: "qcow2", filename: "disk.qcow2", mimeType: "application/x-qemu-disk", @@ -329,7 +324,9 @@ var ( basePartitionTables: defaultBasePartitionTables, requiredPartitionSizes: requiredDirectorySizes, } +} +var ( vmdkDefaultImageConfig = &distro.ImageConfig{ Locale: common.ToPtr("en_US.UTF-8"), EnabledServices: []string{ @@ -339,8 +336,10 @@ var ( "cloud-init-local.service", }, } +) - vmdkImgType = imageType{ +func mkVmdkImgType(d distribution) imageType { + return imageType{ name: "vmdk", filename: "disk.vmdk", mimeType: "application/x-vmdk", @@ -358,8 +357,10 @@ var ( basePartitionTables: defaultBasePartitionTables, requiredPartitionSizes: requiredDirectorySizes, } +} - ovaImgType = imageType{ +func mkOvaImgType(d distribution) imageType { + return imageType{ name: "ova", filename: "image.ova", mimeType: "application/ovf", @@ -377,8 +378,10 @@ var ( basePartitionTables: defaultBasePartitionTables, requiredPartitionSizes: requiredDirectorySizes, } +} - containerImgType = imageType{ +func mkContainerImgType(d distribution) imageType { + return imageType{ name: "container", filename: "container.tar", mimeType: "application/x-tar", @@ -398,8 +401,10 @@ var ( exports: []string{"container"}, requiredPartitionSizes: requiredDirectorySizes, } +} - wslImgType = imageType{ +func mkWslImgType(d distribution) imageType { + return imageType{ name: "wsl", filename: "wsl.tar", mimeType: "application/x-tar", @@ -424,8 +429,10 @@ var ( exports: []string{"container"}, requiredPartitionSizes: requiredDirectorySizes, } +} - minimalrawImgType = imageType{ +func mkMinimalRawImgType(d distribution) imageType { + return imageType{ name: "minimal-raw", filename: "disk.raw.xz", compression: "xz", @@ -434,7 +441,7 @@ var ( osPkgsKey: packageSetLoader, }, defaultImageConfig: &distro.ImageConfig{ - EnabledServices: minimalRawServices, + EnabledServices: minimalServicesForVersion(&d), // NOTE: temporary workaround for a bug in initial-setup that // requires a kickstart file in the root directory. Files: []*fsnode.File{initialSetupKickstart()}, @@ -442,6 +449,7 @@ var ( // Overwrite the default Grub2 timeout value. Timeout: 5, }, + InstallWeakDeps: common.ToPtr(common.VersionLessThan(d.osVersion, VERSION_MINIMAL_WEAKDEPS)), }, rpmOstree: false, kernelOptions: defaultKernelOptions, @@ -454,7 +462,7 @@ var ( basePartitionTables: minimalrawPartitionTables, requiredPartitionSizes: requiredDirectorySizes, } -) +} type distribution struct { name string @@ -473,6 +481,8 @@ var defaultDistroImageConfig = &distro.ImageConfig{ Timezone: common.ToPtr("UTC"), Locale: common.ToPtr("C.UTF-8"), DefaultOSCAPDatastream: common.ToPtr(oscap.DefaultFedoraDatastream()), + InstallWeakDeps: common.ToPtr(true), + MachineIdUninitialized: common.ToPtr(true), } func defaultDistroInstallerConfig(d *distribution) *distro.InstallerConfig { @@ -663,6 +673,8 @@ func newDistro(version int) distro.Distro { distro: &rd, } + qcow2ImgType := mkQcow2ImgType(rd) + ociImgType := qcow2ImgType ociImgType.name = "oci" @@ -696,7 +708,7 @@ func newDistro(version int) distro.Distro { } vhdImgType.defaultImageConfig = vhdConfig.InheritFrom(qcow2ImgType.defaultImageConfig) - minimalrawZstdImgType := minimalrawImgType + minimalrawZstdImgType := mkMinimalRawImgType(rd) minimalrawZstdImgType.name = "minimal-raw-zst" minimalrawZstdImgType.filename = "disk.raw.zst" minimalrawZstdImgType.mimeType = "application/zstd" @@ -744,7 +756,7 @@ func newDistro(version int) distro.Distro { ImageFormat: platform.FORMAT_VMDK, }, }, - vmdkImgType, + mkVmdkImgType(rd), ) x86_64.addImageTypes( &platform.X86{ @@ -754,7 +766,7 @@ func newDistro(version int) distro.Distro { ImageFormat: platform.FORMAT_OVA, }, }, - ovaImgType, + mkOvaImgType(rd), ) x86_64.addImageTypes( &platform.X86{ @@ -768,15 +780,22 @@ func newDistro(version int) distro.Distro { ) x86_64.addImageTypes( &platform.X86{}, - containerImgType, - wslImgType, + mkContainerImgType(rd), + mkWslImgType(rd), ) // add distro installer configuration to all installer types distroInstallerConfig := defaultDistroInstallerConfig(&rd) + + liveInstallerImgType := mkLiveInstallerImgType(rd) liveInstallerImgType.defaultInstallerConfig = distroInstallerConfig + + imageInstallerImgType := mkImageInstallerImgType(rd) imageInstallerImgType.defaultInstallerConfig = distroInstallerConfig + + iotInstallerImgType := mkIotInstallerImgType(rd) iotInstallerImgType.defaultInstallerConfig = distroInstallerConfig + x86_64.addImageTypes( &platform.X86{ BasePlatform: platform.BasePlatform{ @@ -790,8 +809,8 @@ func newDistro(version int) distro.Distro { BIOS: true, UEFIVendor: "fedora", }, - iotOCIImgType, - iotCommitImgType, + mkIotOCIImgType(rd), + mkIotCommitImgType(rd), iotInstallerImgType, imageInstallerImgType, liveInstallerImgType, @@ -804,7 +823,7 @@ func newDistro(version int) distro.Distro { BIOS: false, UEFIVendor: "fedora", }, - iotRawImgType, + mkIotRawImgType(rd), ) x86_64.addImageTypes( &platform.X86{ @@ -814,7 +833,7 @@ func newDistro(version int) distro.Distro { BIOS: false, UEFIVendor: "fedora", }, - iotQcow2ImgType, + mkIotQcow2ImgType(rd), ) aarch64.addImageTypes( &platform.Aarch64{ @@ -833,7 +852,7 @@ func newDistro(version int) distro.Distro { QCOW2Compat: "1.1", }, }, - iotQcow2ImgType, + mkIotQcow2ImgType(rd), ociImgType, qcow2ImgType, ) @@ -848,7 +867,7 @@ func newDistro(version int) distro.Distro { ) aarch64.addImageTypes( &platform.Aarch64{}, - containerImgType, + mkContainerImgType(rd), ) aarch64.addImageTypes( &platform.Aarch64{ @@ -865,9 +884,9 @@ func newDistro(version int) distro.Distro { UEFIVendor: "fedora", }, imageInstallerImgType, - iotCommitImgType, + mkIotCommitImgType(rd), iotInstallerImgType, - iotOCIImgType, + mkIotOCIImgType(rd), liveInstallerImgType, ) aarch64.addImageTypes( @@ -909,7 +928,7 @@ func newDistro(version int) distro.Distro { {"/usr/lib/ostree-boot/efi/start_x.elf", "/boot/efi/"}, }, }, - iotRawImgType, + mkIotRawImgType(rd), ) x86_64.addImageTypes( &platform.X86{ @@ -918,7 +937,7 @@ func newDistro(version int) distro.Distro { ImageFormat: platform.FORMAT_RAW, }, }, - minimalrawImgType, + mkMinimalRawImgType(rd), minimalrawZstdImgType, ) aarch64.addImageTypes( @@ -936,11 +955,13 @@ func newDistro(version int) distro.Distro { {"/usr/share/uboot/rpi_arm64/u-boot.bin", "/boot/efi/rpi-u-boot.bin"}, }, }, - minimalrawImgType, + mkMinimalRawImgType(rd), minimalrawZstdImgType, ) + iotSimplifiedInstallerImgType := mkIotSimplifiedInstallerImgType(rd) iotSimplifiedInstallerImgType.defaultInstallerConfig = distroInstallerConfig + x86_64.addImageTypes( &platform.X86{ BasePlatform: platform.BasePlatform{ @@ -1002,7 +1023,7 @@ func newDistro(version int) distro.Distro { BIOS: true, UEFIVendor: "fedora", }, - iotBootableContainer, + mkIotBootableContainer(rd), ) aarch64.addImageTypes( &platform.Aarch64{ @@ -1018,7 +1039,7 @@ func newDistro(version int) distro.Distro { }, UEFIVendor: "fedora", }, - iotBootableContainer, + mkIotBootableContainer(rd), ) ppc64le.addImageTypes( @@ -1029,7 +1050,7 @@ func newDistro(version int) distro.Distro { QCOW2Compat: "1.1", }, }, - iotBootableContainer, + mkIotBootableContainer(rd), ) s390x.addImageTypes( @@ -1040,7 +1061,7 @@ func newDistro(version int) distro.Distro { QCOW2Compat: "1.1", }, }, - iotBootableContainer, + mkIotBootableContainer(rd), ) ppc64le.addImageTypes( @@ -1055,7 +1076,7 @@ func newDistro(version int) distro.Distro { ) ppc64le.addImageTypes( &platform.PPC64LE{}, - containerImgType, + mkContainerImgType(rd), ) s390x.addImageTypes( @@ -1070,14 +1091,14 @@ func newDistro(version int) distro.Distro { ) s390x.addImageTypes( &platform.S390X{}, - containerImgType, + mkContainerImgType(rd), ) // XXX: there is no "qcow2" for riscv64 yet because there is // no "@Fedora Cloud Server" group riscv64.addImageTypes( &platform.RISCV64{}, - containerImgType, + mkContainerImgType(rd), ) riscv64.addImageTypes( &platform.RISCV64{ @@ -1086,7 +1107,7 @@ func newDistro(version int) distro.Distro { ImageFormat: platform.FORMAT_RAW, }, }, - minimalrawImgType, + mkMinimalRawImgType(rd), minimalrawZstdImgType, ) @@ -1119,3 +1140,44 @@ func DistroFactory(idStr string) distro.Distro { return newDistro(id.MajorVersion) } + +func iotServicesForVersion(d *distribution) []string { + services := []string{ + "NetworkManager.service", + "firewalld.service", + "sshd.service", + "greenboot-grub2-set-counter", + "greenboot-grub2-set-success", + "greenboot-healthcheck", + "greenboot-rpm-ostree-grub2-check-fallback", + "greenboot-status", + "greenboot-task-runner", + "redboot-auto-reboot", + "redboot-task-runner", + } + + if common.VersionLessThan(d.osVersion, "42") { + services = append(services, []string{ + "zezere_ignition.timer", + "zezere_ignition_banner.service", + "parsec", + "dbus-parsec", + }...) + } + + return services +} + +func minimalServicesForVersion(d *distribution) []string { + services := []string{ + "NetworkManager.service", + "initial-setup.service", + "sshd.service", + } + + if common.VersionLessThan(d.osVersion, "43") { + services = append(services, []string{"firewalld.service"}...) + } + + return services +} diff --git a/vendor/github.com/osbuild/images/pkg/distro/fedora/images.go b/vendor/github.com/osbuild/images/pkg/distro/fedora/images.go index 1fae403fe..073c46166 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/fedora/images.go +++ b/vendor/github.com/osbuild/images/pkg/distro/fedora/images.go @@ -110,6 +110,10 @@ func osCustomizations( osc.Hostname = "localhost.localdomain" } + if imageConfig.InstallWeakDeps != nil { + osc.InstallWeakDeps = *imageConfig.InstallWeakDeps + } + timezone, ntpServers := c.GetTimezoneSettings() if timezone != nil { osc.Timezone = *timezone @@ -234,6 +238,10 @@ func osCustomizations( osc.CACerts = ca.PEMCerts } + if imageConfig.MachineIdUninitialized != nil { + osc.MachineIdUninitialized = *imageConfig.MachineIdUninitialized + } + return osc, nil } @@ -334,7 +342,7 @@ func diskImage(workload workload.Workload, img.Compression = t.compression if bp.Minimal { // Disable weak dependencies if the 'minimal' option is enabled - img.InstallWeakDeps = common.ToPtr(false) + img.OSCustomizations.InstallWeakDeps = false } // TODO: move generation into LiveImage pt, err := t.getPartitionTable(bp.Customizations, options, rng) @@ -345,9 +353,6 @@ func diskImage(workload workload.Workload, img.Filename = t.Filename() - d := t.arch.distro - img.FirstBoot = common.VersionGreaterThanOrEqual(d.osVersion, VERSION_FIRSTBOOT) - return img, nil } diff --git a/vendor/github.com/osbuild/images/pkg/distro/fedora/imagetype.go b/vendor/github.com/osbuild/images/pkg/distro/fedora/imagetype.go index 5ea81478b..bd825c301 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/fedora/imagetype.go +++ b/vendor/github.com/osbuild/images/pkg/distro/fedora/imagetype.go @@ -254,11 +254,22 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint, w := t.workload if w == nil { + // XXX: this needs to get duplicaed in exactly the same + // way in rhel/imagetype.go + workloadRepos := payloadRepos + customRepos, err := bp.Customizations.GetRepositories() + if err != nil { + return nil, nil, err + } + installFromRepos := blueprint.RepoCustomizationsInstallFromOnly(customRepos) + workloadRepos = append(workloadRepos, installFromRepos...) + cw := &workload.Custom{ BaseWorkload: workload.BaseWorkload{ - Repos: payloadRepos, + Repos: workloadRepos, }, - Packages: bp.GetPackagesEx(false), + Packages: bp.GetPackagesEx(false), + EnabledModules: bp.GetEnabledModules(), } if services := bp.Customizations.GetServices(); services != nil { cw.Services = services.Enabled diff --git a/vendor/github.com/osbuild/images/pkg/distro/fedora/package_sets.go b/vendor/github.com/osbuild/images/pkg/distro/fedora/package_sets.go index 56bf8dcab..c7cefcab2 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/fedora/package_sets.go +++ b/vendor/github.com/osbuild/images/pkg/distro/fedora/package_sets.go @@ -6,5 +6,5 @@ import ( ) func packageSetLoader(t *imageType) rpmmd.PackageSet { - return packagesets.Load(t, VersionReplacements()) + return packagesets.Load(t, "", VersionReplacements()) } diff --git a/vendor/github.com/osbuild/images/pkg/distro/fedora/version.go b/vendor/github.com/osbuild/images/pkg/distro/fedora/version.go index 26eaab2cb..7aa695fd7 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/fedora/version.go +++ b/vendor/github.com/osbuild/images/pkg/distro/fedora/version.go @@ -11,6 +11,9 @@ const VERSION_ROOTFS_SQUASHFS = "41" // other Fedora variants. const VERSION_FIRSTBOOT = "43" +// Version at which we stop installing weak dependencies for Fedora Minimal +const VERSION_MINIMAL_WEAKDEPS = "43" + func VersionReplacements() map[string]string { return map[string]string{ "VERSION_BRANCHED": VERSION_BRANCHED, diff --git a/vendor/github.com/osbuild/images/pkg/distro/image_config.go b/vendor/github.com/osbuild/images/pkg/distro/image_config.go index 8cee15e3e..ee91acf7e 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/image_config.go +++ b/vendor/github.com/osbuild/images/pkg/distro/image_config.go @@ -94,6 +94,15 @@ type ImageConfig struct { LockRootUser *bool IgnitionPlatform *string + + // InstallWeakDeps enables installation of weak dependencies for packages + // that are statically defined for the pipeline. + InstallWeakDeps *bool + + // How to handle the /etc/machine-id file, when set to true it causes the + // machine id to be set to 'uninitialized' which causes ConditionFirstboot + // to be triggered in systemd + MachineIdUninitialized *bool } // InheritFrom inherits unset values from the provided parent configuration and diff --git a/vendor/github.com/osbuild/images/pkg/distro/packagesets/fedora/package_sets.yaml b/vendor/github.com/osbuild/images/pkg/distro/packagesets/fedora/package_sets.yaml index 994f805ae..6d81dd626 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/packagesets/fedora/package_sets.yaml +++ b/vendor/github.com/osbuild/images/pkg/distro/packagesets/fedora/package_sets.yaml @@ -102,7 +102,6 @@ iot_commit: &iot_commit - "criu" - "cryptsetup" - "curl" - - "dbus-parsec" - "dosfstools" - "dracut-config-generic" - "dracut-network" @@ -131,7 +130,6 @@ iot_commit: &iot_commit - "iputils" - "iwd" - "iwlwifi-mvm-firmware" - - "kernel-tools" - "keyutils" - "less" - "libsss_sudo" @@ -142,11 +140,9 @@ iot_commit: &iot_commit - "openssh-clients" - "openssh-server" - "openssl" - - "parsec" - "pinentry" - "podman" - "policycoreutils" - - "policycoreutils-python-utils" - "polkit" - "procps-ng" - "realtek-firmware" @@ -175,13 +171,19 @@ iot_commit: &iot_commit - "wpa_supplicant" - "xfsprogs" - "xz" - - "zezere-ignition" - "zram-generator" condition: version_less_than: "41": include: - "dnsmasq" + "42": + include: + - "dbus-parsec" + - "kernel-tools" + - "parsec" + - "policycoreutils-python-utils" + - "zezere-ignition" "43": include: - "basesystem" diff --git a/vendor/github.com/osbuild/images/pkg/distro/packagesets/loader.go b/vendor/github.com/osbuild/images/pkg/distro/packagesets/loader.go index 561afa0da..09cad2336 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/packagesets/loader.go +++ b/vendor/github.com/osbuild/images/pkg/distro/packagesets/loader.go @@ -3,17 +3,21 @@ package packagesets import ( "embed" "fmt" + "io/fs" "path/filepath" "strings" + "gopkg.in/yaml.v3" + "github.com/osbuild/images/internal/common" "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/rpmmd" - "gopkg.in/yaml.v3" ) //go:embed */*.yaml -var Data embed.FS +var data embed.FS + +var DataFS fs.FS = data type packageSet struct { Include []string `yaml:"include"` @@ -25,19 +29,31 @@ type conditions struct { Architecture map[string]packageSet `yaml:"architecture,omitempty"` VersionLessThan map[string]packageSet `yaml:"version_less_than,omitempty"` VersionGreaterOrEqual map[string]packageSet `yaml:"version_greater_or_equal,omitempty"` + DistroName map[string]packageSet `yaml:"distro_name,omitempty"` } -func Load(it distro.ImageType, replacements map[string]string) rpmmd.PackageSet { - typeName := strings.ReplaceAll(it.Name(), "-", "_") +// Load loads the PackageSet from the yaml source file discovered via the +// imagetype. By default the imagetype name is used to load the packageset +// but with "overrideTypeName" this can be overriden (useful for e.g. +// installer image types). +func Load(it distro.ImageType, overrideTypeName string, replacements map[string]string) rpmmd.PackageSet { + typeName := it.Name() + if overrideTypeName != "" { + typeName = overrideTypeName + } + typeName = strings.ReplaceAll(typeName, "-", "_") arch := it.Arch() archName := arch.Name() distribution := arch.Distro() distroNameVer := distribution.Name() - distroName := strings.SplitN(distroNameVer, "-", 2)[0] + // we need to split from the right for "centos-stream-10" like + // distro names, sadly go has no rsplit() so we do it manually + // XXX: we cannot use distroidparser here because of import cycles + distroName := distroNameVer[:strings.LastIndex(distroNameVer, "-")] distroVersion := distribution.OsVersion() - distroSets, err := Data.Open(filepath.Join(distroName, "package_sets.yaml")) + distroSets, err := DataFS.Open(filepath.Join(distroName, "package_sets.yaml")) if err != nil { panic(err) } @@ -67,6 +83,12 @@ func Load(it distro.ImageType, replacements map[string]string) rpmmd.PackageSet Exclude: archSet.Exclude, }) } + if distroNameSet, ok := pkgSet.Condition.DistroName[distroName]; ok { + rpmmdPkgSet = rpmmdPkgSet.Append(rpmmd.PackageSet{ + Include: distroNameSet.Include, + Exclude: distroNameSet.Exclude, + }) + } for ltVer, ltSet := range pkgSet.Condition.VersionLessThan { if r, ok := replacements[ltVer]; ok { diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go index 26710bf1c..51ae18012 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go @@ -279,6 +279,10 @@ func osCustomizations( osc.CACerts = ca.PEMCerts } + if imageConfig.InstallWeakDeps != nil { + osc.InstallWeakDeps = *imageConfig.InstallWeakDeps + } + return osc, nil } diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/imagetype.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/imagetype.go index c20008a73..597436cee 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/imagetype.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/imagetype.go @@ -304,11 +304,22 @@ func (t *ImageType) Manifest(bp *blueprint.Blueprint, w := t.Workload if w == nil { + // XXX: this needs to get duplicaed in exactly the same + // way in fedora/imagetype.go + workloadRepos := payloadRepos + customRepos, err := bp.Customizations.GetRepositories() + if err != nil { + return nil, nil, err + } + installFromRepos := blueprint.RepoCustomizationsInstallFromOnly(customRepos) + workloadRepos = append(workloadRepos, installFromRepos...) + cw := &workload.Custom{ BaseWorkload: workload.BaseWorkload{ - Repos: payloadRepos, + Repos: workloadRepos, }, - Packages: bp.GetPackagesEx(false), + Packages: bp.GetPackagesEx(false), + EnabledModules: bp.GetEnabledModules(), } if services := bp.Customizations.GetServices(); services != nil { cw.Services = services.Enabled diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/distro.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/distro.go index 618d61da8..833d09db2 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/distro.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/distro.go @@ -66,6 +66,7 @@ func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { }, }, DefaultOSCAPDatastream: common.ToPtr(oscap.DefaultRHEL10Datastream(d.IsRHEL())), + InstallWeakDeps: common.ToPtr(true), } } diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel7/distro.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel7/distro.go index 6a113f3f5..ea2b09180 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel7/distro.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel7/distro.go @@ -33,6 +33,7 @@ func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { }, KernelOptionsBootloader: common.ToPtr(true), NoBLS: common.ToPtr(true), // RHEL 7 grub does not support BLS + InstallWeakDeps: common.ToPtr(true), } } diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel8/distro.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel8/distro.go index 4c4a1e359..9d8618562 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel8/distro.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel8/distro.go @@ -54,6 +54,7 @@ func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { }, KernelOptionsBootloader: common.ToPtr(true), DefaultOSCAPDatastream: common.ToPtr(oscap.DefaultRHEL8Datastream(d.IsRHEL())), + InstallWeakDeps: common.ToPtr(true), } } diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel9/distro.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel9/distro.go index 06488134d..fe0eb69a6 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel9/distro.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel9/distro.go @@ -69,6 +69,7 @@ func defaultDistroImageConfig(d *rhel.Distribution) *distro.ImageConfig { }, }, DefaultOSCAPDatastream: common.ToPtr(oscap.DefaultRHEL9Datastream(d.IsRHEL())), + InstallWeakDeps: common.ToPtr(true), } } diff --git a/vendor/github.com/osbuild/images/pkg/dnfjson/dnfjson.go b/vendor/github.com/osbuild/images/pkg/dnfjson/dnfjson.go index c20f29927..5e47e8c23 100644 --- a/vendor/github.com/osbuild/images/pkg/dnfjson/dnfjson.go +++ b/vendor/github.com/osbuild/images/pkg/dnfjson/dnfjson.go @@ -168,6 +168,7 @@ type Solver struct { // DepsolveResult contains the results of a depsolve operation. type DepsolveResult struct { Packages []rpmmd.PackageSpec + Modules []rpmmd.ModuleSpec Repos []rpmmd.RepoConfig SBOM *sbom.Document Solver string @@ -240,7 +241,7 @@ func (s *Solver) Depsolve(pkgSets []rpmmd.PackageSet, sbomType sbom.StandardType return nil, fmt.Errorf("decoding depsolve result failed: %w", err) } - packages, repos := result.toRPMMD(rhsmMap) + packages, modules, repos := result.toRPMMD(rhsmMap) var sbomDoc *sbom.Document if sbomType != sbom.StandardTypeNone { @@ -252,6 +253,7 @@ func (s *Solver) Depsolve(pkgSets []rpmmd.PackageSet, sbomType sbom.StandardType return &DepsolveResult{ Packages: packages, + Modules: modules, Repos: repos, SBOM: sbomDoc, Solver: result.Solver, @@ -463,9 +465,10 @@ func (s *Solver) makeDepsolveRequest(pkgSets []rpmmd.PackageSet, sbomType sbom.S transactions := make([]transactionArgs, len(pkgSets)) for dsIdx, pkgSet := range pkgSets { transactions[dsIdx] = transactionArgs{ - PackageSpecs: pkgSet.Include, - ExcludeSpecs: pkgSet.Exclude, - InstallWeakDeps: pkgSet.InstallWeakDeps, + PackageSpecs: pkgSet.Include, + ExcludeSpecs: pkgSet.Exclude, + ModuleEnableSpecs: pkgSet.EnabledModules, + InstallWeakDeps: pkgSet.InstallWeakDeps, } for _, jobRepo := range pkgSet.Repositories { @@ -577,7 +580,7 @@ func (s *Solver) makeSearchRequest(repos []rpmmd.RepoConfig, packages []string) // convert internal a list of PackageSpecs and map of repoConfig to the rpmmd // equivalents and attach key and subscription information based on the // repository configs. -func (result depsolveResult) toRPMMD(rhsm map[string]bool) ([]rpmmd.PackageSpec, []rpmmd.RepoConfig) { +func (result depsolveResult) toRPMMD(rhsm map[string]bool) ([]rpmmd.PackageSpec, []rpmmd.ModuleSpec, []rpmmd.RepoConfig) { pkgs := result.Packages repos := result.Repos rpmDependencies := make([]rpmmd.PackageSpec, len(pkgs)) @@ -610,6 +613,22 @@ func (result depsolveResult) toRPMMD(rhsm map[string]bool) ([]rpmmd.PackageSpec, } } + mods := result.Modules + moduleSpecs := make([]rpmmd.ModuleSpec, len(mods)) + + i := 0 + for _, mod := range mods { + moduleSpecs[i].ModuleConfigFile.Data.Name = mod.ModuleConfigFile.Data.Name + moduleSpecs[i].ModuleConfigFile.Data.Stream = mod.ModuleConfigFile.Data.Stream + moduleSpecs[i].ModuleConfigFile.Data.State = mod.ModuleConfigFile.Data.State + moduleSpecs[i].ModuleConfigFile.Data.Profiles = mod.ModuleConfigFile.Data.Profiles + + moduleSpecs[i].FailsafeFile.Path = mod.FailsafeFile.Path + moduleSpecs[i].FailsafeFile.Data = mod.FailsafeFile.Data + + i++ + } + repoConfigs := make([]rpmmd.RepoConfig, 0, len(repos)) for repoID := range repos { repo := repos[repoID] @@ -635,7 +654,7 @@ func (result depsolveResult) toRPMMD(rhsm map[string]bool) ([]rpmmd.PackageSpec, SSLClientCert: repo.SSLClientCert, }) } - return rpmDependencies, repoConfigs + return rpmDependencies, moduleSpecs, repoConfigs } // Request command and arguments for dnf-json @@ -723,6 +742,9 @@ type transactionArgs struct { // Packages to exclude from results ExcludeSpecs []string `json:"exclude-specs"` + // Modules to enable during depsolve + ModuleEnableSpecs []string `json:"module-enable-specs,omitempty"` + // IDs of repositories to use for this depsolve RepoIDs []string `json:"repo-ids"` @@ -779,7 +801,7 @@ type ModuleConfigData struct { type ModuleFailsafeFile struct { Path string `json:"path"` - Data string `json:"string"` + Data string `json:"data"` } // dnf-json error structure diff --git a/vendor/github.com/osbuild/images/pkg/experimentalflags/experimental.go b/vendor/github.com/osbuild/images/pkg/experimentalflags/experimental.go new file mode 100644 index 000000000..b3e10b495 --- /dev/null +++ b/vendor/github.com/osbuild/images/pkg/experimentalflags/experimental.go @@ -0,0 +1,69 @@ +// Package experimentalflags provides functionality for reading +// options defined in an environment variable named +// IMAGE_BUILDER_EXPERIMENTAL. +// +// These functions should be used to determine, in a common way, if +// experimental features should be enabled when using the libarary. +package experimentalflags + +import ( + "os" + "strconv" + "strings" +) + +const envKEY = "IMAGE_BUILDER_EXPERIMENTAL" + +func experimentalOptions() map[string]string { + expMap := map[string]string{} + + env := os.Getenv(envKEY) + if env == "" { + return expMap + } + + for _, s := range strings.Split(env, ",") { + l := strings.SplitN(s, "=", 2) + switch len(l) { + case 1: + expMap[l[0]] = "true" + case 2: + expMap[l[0]] = l[1] + } + } + + return expMap +} + +// Bool returns true if there is a boolean option with the given +// option name. +// +// Example usage by the user: +// +// IMAGE_BUILDER_EXPERIMENTAL=skip-foo,skip-bar=1,skip-baz=true +// +// would result in experimetnalflags.Bool("skip-foo") -> true +func Bool(option string) bool { + expMap := experimentalOptions() + b, err := strconv.ParseBool(expMap[option]) + if err != nil { + // not much we can do for invalid inputs, just assume false + return false + } + return b +} + +// String returns the user set string for the given experimental feature. +// +// Note that currently no quoting or escaping is supported, so a string +// can (currently) not contain a "," or a "=". +// +// Example usage by the user: +// +// IMAGE_BUILDER_EXPERIMENTAL=key=value +// +// would result in experimetnalflags.String("key") -> "value" +func String(option string) string { + expMap := experimentalOptions() + return expMap[option] +} diff --git a/vendor/github.com/osbuild/images/pkg/image/disk.go b/vendor/github.com/osbuild/images/pkg/image/disk.go index f624e2ff8..660ea6d8a 100644 --- a/vendor/github.com/osbuild/images/pkg/image/disk.go +++ b/vendor/github.com/osbuild/images/pkg/image/disk.go @@ -39,8 +39,6 @@ type DiskImage struct { // InstallWeakDeps enables installation of weak dependencies for packages // that are statically defined for the payload pipeline of the image. InstallWeakDeps *bool - - FirstBoot bool } func NewDiskImage() *DiskImage { @@ -65,10 +63,10 @@ func (img *DiskImage) InstantiateManifest(m *manifest.Manifest, osPipeline.OSProduct = img.OSProduct osPipeline.OSVersion = img.OSVersion osPipeline.OSNick = img.OSNick + if img.InstallWeakDeps != nil { osPipeline.InstallWeakDeps = *img.InstallWeakDeps } - osPipeline.FirstBoot = img.FirstBoot rawImagePipeline := manifest.NewRawImage(buildPipeline, osPipeline) rawImagePipeline.PartTool = img.PartTool diff --git a/vendor/github.com/osbuild/images/pkg/manifest/build.go b/vendor/github.com/osbuild/images/pkg/manifest/build.go index 9f91c9942..2a36d8ef2 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/build.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/build.go @@ -3,7 +3,9 @@ package manifest import ( "fmt" + "github.com/osbuild/images/internal/common" "github.com/osbuild/images/pkg/container" + "github.com/osbuild/images/pkg/experimentalflags" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/runner" @@ -57,6 +59,11 @@ func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts repos: filterRepos(repos, name), containerBuildable: opts.ContainerBuildable, } + + // This allows to bootstrap the buildroot with a custom container + // for e.g. cross-arch-build experiments, + maybeAddExperimentalContainerBootstrap(m, runner, opts, pipeline) + m.addPipeline(pipeline) return pipeline } @@ -149,6 +156,40 @@ func (p *BuildrootFromPackages) getSELinuxLabels() map[string]string { return labels } +// maybeAddExperimentalContainerBootstrap will return a container buildroot +// if the "IMAGE_BUILDER_EXPERIMENTAL=bootstrap=" is +// defined. This allows us to do cross-arch build experimentation. +// +// A "bootstrap" container has only these requirements: +// - python3 for the runners +// - rpm so that the real buildroot rpms can get installed +// - setfiles so that the selinux stage for the real buildroot can run +// (and does not even need a working dnf or repo setup). +func maybeAddExperimentalContainerBootstrap(m *Manifest, runner runner.Runner, opts *BuildOptions, build *BuildrootFromPackages) { + bootstrapBuildrootRef := experimentalflags.String("bootstrap") + if bootstrapBuildrootRef == "" { + return + } + + cntSrcs := []container.SourceSpec{ + { + Source: bootstrapBuildrootRef, + Name: bootstrapBuildrootRef, + TLSVerify: common.ToPtr(false), + }, + } + name := "bootstrap-buildroot" + bootstrapPipeline := &BuildrootFromContainer{ + Base: NewBase(name, nil), + runner: runner, + dependents: make([]Pipeline, 0), + containers: cntSrcs, + disableSelinux: true, + } + m.addPipeline(bootstrapPipeline) + build.build = bootstrapPipeline +} + type BuildrootFromContainer struct { Base @@ -159,6 +200,7 @@ type BuildrootFromContainer struct { containerSpecs []container.Spec containerBuildable bool + disableSelinux bool } // NewBuildFromContainer creates a new build pipeline from the given @@ -213,6 +255,10 @@ func (p *BuildrootFromContainer) serializeEnd() { } func (p *BuildrootFromContainer) getSELinuxLabels() map[string]string { + if p.disableSelinux { + return nil + } + labels := map[string]string{ "/usr/bin/ostree": "system_u:object_r:install_exec_t:s0", } @@ -242,13 +288,15 @@ func (p *BuildrootFromContainer) serialize() osbuild.Pipeline { panic(err) } pipeline.AddStage(stage) - pipeline.AddStage(osbuild.NewSELinuxStage( - &osbuild.SELinuxStageOptions{ - FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", - ExcludePaths: []string{"/sysroot"}, - Labels: p.getSELinuxLabels(), - }, - )) + if !p.disableSelinux { + pipeline.AddStage(osbuild.NewSELinuxStage( + &osbuild.SELinuxStageOptions{ + FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", + ExcludePaths: []string{"/sysroot"}, + Labels: p.getSELinuxLabels(), + }, + )) + } return pipeline } diff --git a/vendor/github.com/osbuild/images/pkg/manifest/manifest.go b/vendor/github.com/osbuild/images/pkg/manifest/manifest.go index 63b16bf75..985263c11 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/manifest.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/manifest.go @@ -22,15 +22,6 @@ import ( "github.com/osbuild/images/pkg/rpmmd" ) -type Arch uint64 - -const ( - ARCH_X86_64 Arch = iota - ARCH_AARCH64 - ARCH_S390X - ARCH_PPC64LE -) - type Distro uint64 const ( diff --git a/vendor/github.com/osbuild/images/pkg/manifest/os.go b/vendor/github.com/osbuild/images/pkg/manifest/os.go index 5a3039800..5ea239d03 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/os.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/os.go @@ -149,10 +149,14 @@ type OSCustomizations struct { // instead of BLS. Required for legacy systems like RHEL 7. NoBLS bool - // FirstBoot sets if the machine-id should be written with the - // magic value that determines if the machine is being booted for the - // first time. - FirstBoot bool + // InstallWeakDeps enables installation of weak dependencies for packages + // that are statically defined for the pipeline. + // Defaults to True. + InstallWeakDeps bool + + // Determines if the machine id should be set to "uninitialized" which allows + // "ConditionFirstBoot" to work in systemd + MachineIdUninitialized bool } // OS represents the filesystem tree of the target image. This roughly @@ -184,6 +188,7 @@ type OS struct { // content-related fields repos []rpmmd.RepoConfig packageSpecs []rpmmd.PackageSpec + moduleSpecs []rpmmd.ModuleSpec containerSpecs []container.Spec ostreeParentSpec *ostree.CommitSpec @@ -193,11 +198,6 @@ type OS struct { OSProduct string OSVersion string OSNick string - - // InstallWeakDeps enables installation of weak dependencies for packages - // that are statically defined for the pipeline. - // Defaults to True. - InstallWeakDeps bool } // NewOS creates a new OS pipeline. build is the build pipeline to use for @@ -206,10 +206,9 @@ type OS struct { func NewOS(buildPipeline Build, platform platform.Platform, repos []rpmmd.RepoConfig) *OS { name := "os" p := &OS{ - Base: NewBase(name, buildPipeline), - repos: filterRepos(repos, name), - platform: platform, - InstallWeakDeps: true, + Base: NewBase(name, buildPipeline), + repos: filterRepos(repos, name), + platform: platform, } buildPipeline.addDependent(p) return p @@ -281,10 +280,17 @@ func (p *OS) getPackageSetChain(Distro) []rpmmd.PackageSet { if p.Workload != nil { workloadPackages := p.Workload.GetPackages() if len(workloadPackages) > 0 { - chain = append(chain, rpmmd.PackageSet{ + ps := rpmmd.PackageSet{ Include: workloadPackages, Repositories: append(osRepos, p.Workload.GetRepos()...), - }) + } + + workloadModules := p.Workload.GetEnabledModules() + if len(workloadModules) > 0 { + ps.EnabledModules = workloadModules + } + + chain = append(chain, ps) } } @@ -387,6 +393,7 @@ func (p *OS) serializeStart(inputs Inputs) { } p.packageSpecs = inputs.Depsolved.Packages + p.moduleSpecs = inputs.Depsolved.Modules p.containerSpecs = inputs.Containers if len(inputs.Commits) > 0 { if len(inputs.Commits) > 1 { @@ -742,6 +749,36 @@ func (p *OS) serialize() osbuild.Pipeline { pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...) } + // write modularity related configuration files + if len(p.moduleSpecs) > 0 { + pipeline.AddStages(osbuild.GenDNFModuleConfigStages(p.moduleSpecs)...) + + var failsafeFiles []*fsnode.File + + // the failsafe file is a blob of YAML returned directly from the depsolver, + // we write them as 'normal files' without a special stage + for _, module := range p.moduleSpecs { + moduleFailsafeFile, err := fsnode.NewFile(module.FailsafeFile.Path, nil, nil, nil, []byte(module.FailsafeFile.Data)) + + if err != nil { + panic("failed to create module failsafe file") + } + + failsafeFiles = append(failsafeFiles, moduleFailsafeFile) + } + + failsafeDir, err := fsnode.NewDirectory("/var/lib/dnf/modulefailsafe", nil, nil, nil, true) + + if err != nil { + panic("failed to create module failsafe directory") + } + + pipeline.AddStages(osbuild.GenDirectoryNodesStages([]*fsnode.Directory{failsafeDir})...) + pipeline.AddStages(osbuild.GenFileNodesStages(failsafeFiles)...) + + p.Files = append(p.Files, failsafeFiles...) + } + enabledServices := []string{} disabledServices := []string{} maskedServices := []string{} @@ -813,7 +850,7 @@ func (p *OS) serialize() osbuild.Pipeline { pipeline.AddStage(osbuild.NewCAStageStage()) } - if p.FirstBoot { + if p.MachineIdUninitialized { pipeline.AddStage(osbuild.NewMachineIdStage(&osbuild.MachineIdStageOptions{ FirstBoot: osbuild.MachineIdFirstBootYes, })) diff --git a/vendor/github.com/osbuild/images/pkg/manifest/raw_bootc.go b/vendor/github.com/osbuild/images/pkg/manifest/raw_bootc.go index a5414d045..39b92e6f6 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/raw_bootc.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/raw_bootc.go @@ -210,11 +210,22 @@ func (p *RawBootcImage) serialize() osbuild.Pipeline { // First create custom directories, because some of the custom files may depend on them if len(p.Directories) > 0 { - pipeline.AddStages(osbuild.GenDirectoryNodesStages(p.Directories)...) + + stages := osbuild.GenDirectoryNodesStages(p.Directories) + for _, stage := range stages { + stage.Mounts = mounts + stage.Devices = devices + } + pipeline.AddStages(stages...) } if len(p.Files) > 0 { - pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...) + stages := osbuild.GenFileNodesStages(p.Files) + for _, stage := range stages { + stage.Mounts = mounts + stage.Devices = devices + } + pipeline.AddStages(stages...) } // XXX: maybe go back to adding this conditionally when we stop diff --git a/vendor/github.com/osbuild/images/pkg/osbuild/dnf_module_config_stage.go b/vendor/github.com/osbuild/images/pkg/osbuild/dnf_module_config_stage.go new file mode 100644 index 000000000..bd037db01 --- /dev/null +++ b/vendor/github.com/osbuild/images/pkg/osbuild/dnf_module_config_stage.go @@ -0,0 +1,62 @@ +package osbuild + +import ( + "github.com/osbuild/images/pkg/rpmmd" +) + +type DNFModuleConfig struct { + Name string `json:"name,omitempty"` + Stream string `json:"stream,omitempty"` + State string `json:"state,omitempty"` + Profiles []string `json:"profiles"` +} + +type DNFModuleConfigStageOptions struct { + Config *DNFModuleConfig `json:"conf,omitempty"` +} + +func (DNFModuleConfigStageOptions) isStageOptions() {} + +// NewDNFModuleConfigStageOptions creates a new DNFConfig Stage options object. +func NewDNFModuleConfigStageOptions(config *DNFModuleConfig) *DNFModuleConfigStageOptions { + return &DNFModuleConfigStageOptions{ + Config: config, + } +} + +func (o DNFModuleConfigStageOptions) validate() error { + return nil +} + +// NewDNFModuleConfigStage creates a new DNFModuleConfig Stage object. +func NewDNFModuleConfigStage(options *DNFModuleConfigStageOptions) *Stage { + if err := options.validate(); err != nil { + panic(err) + } + + return &Stage{ + Type: "org.osbuild.dnf.module-config", + Options: options, + } +} + +func GenDNFModuleConfigStages(modules []rpmmd.ModuleSpec) []*Stage { + stages := make([]*Stage, len(modules)) + + for _, module := range modules { + data := module.ModuleConfigFile.Data + + stage := NewDNFModuleConfigStage(&DNFModuleConfigStageOptions{ + Config: &DNFModuleConfig{ + Name: data.Name, + Stream: data.Stream, + State: data.State, + Profiles: data.Profiles, + }, + }) + + stages = append(stages, stage) + } + + return stages +} diff --git a/vendor/github.com/osbuild/images/pkg/osbuild/machine_id_stage.go b/vendor/github.com/osbuild/images/pkg/osbuild/machine_id_stage.go index 7904d5907..44f5384fc 100644 --- a/vendor/github.com/osbuild/images/pkg/osbuild/machine_id_stage.go +++ b/vendor/github.com/osbuild/images/pkg/osbuild/machine_id_stage.go @@ -3,9 +3,9 @@ package osbuild type MachineIdFirstBoot string const ( - MachineIdFirstBootYes MachineIdFirstBoot = "yes" - MachineIdFirstBootNo MachineIdFirstBoot = "no" - MachineIdFirstBootPreserver MachineIdFirstBoot = "preserve" + MachineIdFirstBootYes MachineIdFirstBoot = "yes" + MachineIdFirstBootNo MachineIdFirstBoot = "no" + MachineIdFirstBootPreserve MachineIdFirstBoot = "preserve" ) type MachineIdStageOptions struct { diff --git a/vendor/github.com/osbuild/images/pkg/osbuild/osbuild-exec.go b/vendor/github.com/osbuild/images/pkg/osbuild/osbuild-exec.go index 112b07f6a..e49929e03 100644 --- a/vendor/github.com/osbuild/images/pkg/osbuild/osbuild-exec.go +++ b/vendor/github.com/osbuild/images/pkg/osbuild/osbuild-exec.go @@ -9,7 +9,10 @@ import ( "os/exec" "strings" + "github.com/osbuild/images/data/dependencies" "github.com/osbuild/images/pkg/datasizes" + + "github.com/hashicorp/go-version" ) // Run an instance of osbuild, returning a parsed osbuild.Result. @@ -18,6 +21,10 @@ import ( // does not return an error in this case. Instead, the failure is communicated // with its corresponding logs through osbuild.Result. func RunOSBuild(manifest []byte, store, outputDirectory string, exports, checkpoints, extraEnv []string, result bool, errorWriter io.Writer) (*Result, error) { + if err := CheckMinimumOSBuildVersion(); err != nil { + return nil, err + } + var stdoutBuffer bytes.Buffer var res Result @@ -96,6 +103,30 @@ func RunOSBuild(manifest []byte, store, outputDirectory string, exports, checkpo return &res, nil } +func CheckMinimumOSBuildVersion() error { + osbuildVersion, err := OSBuildVersion() + if err != nil { + return fmt.Errorf("error getting osbuild version: %v", err) + } + + minVersion, err := version.NewVersion(dependencies.MinimumOSBuildVersion()) + if err != nil { + return fmt.Errorf("error parsing minimum osbuild version: %v", err) + } + + currentVersion, err := version.NewVersion(osbuildVersion) + if err != nil { + return fmt.Errorf("error parsing current osbuild version: %v", err) + } + + if currentVersion.LessThan(minVersion) { + return fmt.Errorf("osbuild version %q is lower than the minimum required version %q", + osbuildVersion, dependencies.MinimumOSBuildVersion()) + } + + return nil +} + // OSBuildVersion returns the version of osbuild. func OSBuildVersion() (string, error) { var stdoutBuffer bytes.Buffer diff --git a/vendor/github.com/osbuild/images/pkg/rpmmd/repository.go b/vendor/github.com/osbuild/images/pkg/rpmmd/repository.go index 8da421bad..87b944989 100644 --- a/vendor/github.com/osbuild/images/pkg/rpmmd/repository.go +++ b/vendor/github.com/osbuild/images/pkg/rpmmd/repository.go @@ -141,6 +141,7 @@ func (pkg Package) ToPackageInfo() PackageInfo { type PackageSet struct { Include []string Exclude []string + EnabledModules []string Repositories []RepoConfig InstallWeakDeps bool } @@ -150,6 +151,7 @@ type PackageSet struct { func (ps PackageSet) Append(other PackageSet) PackageSet { ps.Include = append(ps.Include, other.Include...) ps.Exclude = append(ps.Exclude, other.Exclude...) + ps.EnabledModules = append(ps.EnabledModules, other.EnabledModules...) return ps } @@ -199,6 +201,28 @@ type PackageInfo struct { Dependencies []PackageSpec `json:"dependencies,omitempty"` } +type ModuleSpec struct { + ModuleConfigFile ModuleConfigFile `json:"module-file"` + FailsafeFile ModuleFailsafeFile `json:"failsafe-file"` +} + +type ModuleConfigFile struct { + Path string `json:"path"` + Data ModuleConfigData `json:"data"` +} + +type ModuleConfigData struct { + Name string `json:"name"` + Stream string `json:"stream"` + Profiles []string `json:"profiles"` + State string `json:"state"` +} + +type ModuleFailsafeFile struct { + Path string `json:"path"` + Data string `json:"data"` +} + // GetEVRA returns the package's Epoch:Version-Release.Arch string func (ps *PackageSpec) GetEVRA() string { if ps.Epoch == 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index 8b29d106e..d2efacda8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1037,8 +1037,9 @@ github.com/oracle/oci-go-sdk/v54/identity github.com/oracle/oci-go-sdk/v54/objectstorage github.com/oracle/oci-go-sdk/v54/objectstorage/transfer github.com/oracle/oci-go-sdk/v54/workrequests -# github.com/osbuild/images v0.120.0 +# github.com/osbuild/images v0.123.0 ## explicit; go 1.22.8 +github.com/osbuild/images/data/dependencies github.com/osbuild/images/data/repositories github.com/osbuild/images/internal/common github.com/osbuild/images/internal/environment @@ -1073,6 +1074,7 @@ github.com/osbuild/images/pkg/distro/test_distro github.com/osbuild/images/pkg/distrofactory github.com/osbuild/images/pkg/distroidparser github.com/osbuild/images/pkg/dnfjson +github.com/osbuild/images/pkg/experimentalflags github.com/osbuild/images/pkg/image github.com/osbuild/images/pkg/manifest github.com/osbuild/images/pkg/osbuild