build(deps): bump the go-deps group across 1 directory with 11 updates

---
updated-dependencies:
- dependency-name: cloud.google.com/go/compute
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: cloud.google.com/go/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/aws/aws-sdk-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/hashicorp/go-retryablehttp
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: github.com/openshift-online/ocm-sdk-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: github.com/osbuild/images
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: github.com/vmware/govmomi
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2024-05-21 04:58:46 +00:00 committed by Achilleas Koutsou
parent 295dddca37
commit c1d56a50c2
178 changed files with 52314 additions and 45955 deletions

View file

@ -387,9 +387,21 @@ func (c *Customizations) GetContainerStorage() *ContainerStorageCustomization {
return c.ContainersStorage
}
func (c *Customizations) GetInstaller() *InstallerCustomization {
func (c *Customizations) GetInstaller() (*InstallerCustomization, error) {
if c == nil || c.Installer == nil {
return nil
return nil, nil
}
return c.Installer
// Validate conflicting customizations: Installer options aren't supported
// when the user adds their own kickstart content
if c.Installer.Kickstart != nil && len(c.Installer.Kickstart.Contents) > 0 {
if c.Installer.Unattended {
return nil, fmt.Errorf("installer.unattended is not allowed when adding custom kickstart contents")
}
if len(c.Installer.SudoNopasswd) > 0 {
return nil, fmt.Errorf("installer.sudo-nopasswd is not allowed when adding custom kickstart contents")
}
}
return c.Installer, nil
}

View file

@ -1,6 +1,11 @@
package blueprint
type InstallerCustomization struct {
Unattended bool `json:"unattended,omitempty" toml:"unattended,omitempty"`
SudoNopasswd []string `json:"sudo-nopasswd,omitempty" toml:"sudo-nopasswd,omitempty"`
Unattended bool `json:"unattended,omitempty" toml:"unattended,omitempty"`
SudoNopasswd []string `json:"sudo-nopasswd,omitempty" toml:"sudo-nopasswd,omitempty"`
Kickstart *Kickstart `json:"kickstart,omitempty" toml:"kickstart,omitempty"`
}
type Kickstart struct {
Contents string `json:"contents" toml:"contents"`
}

View file

@ -0,0 +1,12 @@
package bootc
type Config struct {
// Name of the config file
Filename string
// Filesystem type for the root partition
RootFilesystemType string
// Extra kernel args to append
KernelArgs []string
}

View file

@ -0,0 +1,93 @@
package kickstart
import (
"fmt"
"github.com/osbuild/images/pkg/blueprint"
"github.com/osbuild/images/pkg/customizations/users"
)
type File struct {
Contents string
}
type OSTree struct {
OSName string
Remote string
}
type Options struct {
// Path where the kickstart file will be created
Path string
// Add kickstart options to make the installation fully unattended
Unattended bool
// Create a sudoers drop-in file for each user or group to enable the
// NOPASSWD option
SudoNopasswd []string
// Kernel options that will be appended to the installed system
// (not the iso)
KernelOptionsAppend []string
// Enable networking on on boot in the installed system
NetworkOnBoot bool
Language *string
Keyboard *string
Timezone *string
// Users to create during installation
Users []users.User
// Groups to create during installation
Groups []users.Group
// ostree-related kickstart options
OSTree *OSTree
// User-defined kickstart files that will be added to the ISO
UserFile *File
}
func New(customizations *blueprint.Customizations) (*Options, error) {
options := &Options{
Users: users.UsersFromBP(customizations.GetUsers()),
Groups: users.GroupsFromBP(customizations.GetGroups()),
}
instCust, err := customizations.GetInstaller()
if err != nil {
return nil, err
}
if instCust != nil {
options.SudoNopasswd = instCust.SudoNopasswd
options.Unattended = instCust.Unattended
if instCust.Kickstart != nil {
options.UserFile = &File{Contents: instCust.Kickstart.Contents}
}
}
if err := options.Validate(); err != nil {
return nil, err
}
return options, nil
}
func (options Options) Validate() error {
if options.UserFile != nil {
// users, groups, and other kickstart options are not allowed when
// users add their own kickstarts
if options.Unattended {
return fmt.Errorf("kickstart unattended options are not compatible with user-supplied kickstart content")
}
if len(options.SudoNopasswd) > 0 {
return fmt.Errorf("kickstart sudo nopasswd drop-in file creation is not compatible with user-supplied kickstart content")
}
if len(options.Users)+len(options.Groups) > 0 {
return fmt.Errorf("kickstart users and/or groups are not compatible with user-supplied kickstart content")
}
}
return nil
}

View file

@ -8,9 +8,11 @@ import (
"github.com/osbuild/images/internal/workload"
"github.com/osbuild/images/pkg/blueprint"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/bootc"
"github.com/osbuild/images/pkg/customizations/fdo"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/ignition"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/customizations/oscap"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/distro"
@ -414,7 +416,6 @@ func liveInstallerImage(workload workload.Workload,
d := t.arch.distro
img.Product = d.product
img.OSName = "fedora"
img.Variant = "Workstation"
img.OSVersion = d.osVersion
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
@ -443,12 +444,16 @@ func imageInstallerImage(workload workload.Workload,
img := image.NewAnacondaTarInstaller()
if instCust := customizations.GetInstaller(); instCust != nil {
img.NoPasswd = instCust.SudoNopasswd
img.UnattendedKickstart = instCust.Unattended
var err error
img.Kickstart, err = kickstart.New(customizations)
if err != nil {
return nil, err
}
img.Kickstart.Language = &img.OSCustomizations.Language
img.Kickstart.Keyboard = img.OSCustomizations.Keyboard
img.Kickstart.Timezone = &img.OSCustomizations.Timezone
if img.UnattendedKickstart {
if img.Kickstart.Unattended {
// NOTE: this is not supported right now because the
// image-installer on Fedora isn't working when unattended.
// These options are probably necessary but could change.
@ -461,15 +466,12 @@ func imageInstallerImage(workload workload.Workload,
img.Platform = t.platform
img.Workload = workload
var err error
img.OSCustomizations, err = osCustomizations(t, packageSets[osPkgsKey], containers, bp.Customizations)
if err != nil {
return nil, err
}
img.ExtraBasePackages = packageSets[installerPkgsKey]
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())
img.SquashfsCompression = "lz4"
@ -480,8 +482,6 @@ func imageInstallerImage(workload workload.Workload,
// We don't know the variant that goes into the OS pipeline that gets installed
img.Variant = "Unknown"
img.OSName = "fedora"
img.OSVersion = d.osVersion
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
@ -572,6 +572,10 @@ func bootableContainerImage(workload workload.Workload,
img.Filename = t.Filename()
img.InstallWeakDeps = false
img.BootContainer = true
img.BootcConfig = &bootc.Config{
Filename: "20-fedora.toml",
RootFilesystemType: "ext4",
}
return img, nil
}
@ -643,13 +647,20 @@ func iotInstallerImage(workload workload.Workload,
img.FIPS = customizations.GetFIPS()
img.Platform = t.platform
img.ExtraBasePackages = packageSets[installerPkgsKey]
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())
img.Language, img.Keyboard = customizations.GetPrimaryLocale()
img.Kickstart, err = kickstart.New(customizations)
if err != nil {
return nil, err
}
img.Kickstart.OSTree = &kickstart.OSTree{
OSName: "fedora-iot",
Remote: "fedora-iot",
}
img.Kickstart.Path = osbuild.KickstartPathOSBuild
img.Kickstart.Language, img.Kickstart.Keyboard = customizations.GetPrimaryLocale()
// ignore ntp servers - we don't currently support setting these in the
// kickstart though kickstart does support setting them
img.Timezone, _ = customizations.GetTimezoneSettings()
img.Kickstart.Timezone, _ = customizations.GetTimezoneSettings()
img.AdditionalAnacondaModules = []string{
"org.fedoraproject.Anaconda.Modules.Timezone",
@ -657,17 +668,10 @@ func iotInstallerImage(workload workload.Workload,
"org.fedoraproject.Anaconda.Modules.Users",
}
if instCust := customizations.GetInstaller(); instCust != nil {
img.NoPasswd = instCust.SudoNopasswd
img.UnattendedKickstart = instCust.Unattended
}
img.SquashfsCompression = "lz4"
img.Product = d.product
img.Variant = "IoT"
img.OSName = "fedora-iot"
img.Remote = "fedora-iot"
img.OSVersion = d.osVersion
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
img.Preview = common.VersionGreaterThanOrEqual(img.OSVersion, VERSION_BRANCHED)
@ -780,6 +784,8 @@ func iotSimplifiedInstallerImage(workload workload.Workload,
}
}
img.AdditionalDracutModules = append(img.AdditionalDracutModules, "dbus-broker")
d := t.arch.distro
img.Product = d.product
img.Variant = "IoT"

View file

@ -418,10 +418,25 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp
return []string{w}, nil
}
if customizations.GetInstaller() != nil {
instCust, err := customizations.GetInstaller()
if err != nil {
return nil, err
}
if instCust != nil {
// only supported by the Anaconda installer
if slices.Index([]string{"iot-installer"}, t.name) == -1 {
return nil, fmt.Errorf("installer customizations are not supported for %q", t.name)
return nil, fmt.Errorf("installer customizations are not supported for %q", t.Name())
}
// NOTE: the image type check is redundant with the check above, but
// let's keep it explicit in case one of the two changes.
// The kickstart contents is incompatible with the users and groups
// customization only for the iot-installer.
if t.Name() == "iot-installer" &&
instCust.Kickstart != nil &&
len(instCust.Kickstart.Contents) > 0 &&
(customizations.GetUsers() != nil || customizations.GetGroups() != nil) {
return nil, fmt.Errorf("iot-installer installer.kickstart.contents are not supported in combination with users or groups")
}
}

View file

@ -10,6 +10,7 @@ import (
"github.com/osbuild/images/pkg/customizations/fdo"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/ignition"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/customizations/oscap"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/distro"
@ -487,18 +488,19 @@ func EdgeInstallerImage(workload workload.Workload,
img.Platform = t.platform
img.ExtraBasePackages = packageSets[InstallerPkgsKey]
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())
img.Language, img.Keyboard = customizations.GetPrimaryLocale()
img.Kickstart, err = kickstart.New(customizations)
if err != nil {
return nil, err
}
img.Kickstart.OSTree = &kickstart.OSTree{
OSName: "rhel-edge",
}
img.Kickstart.Path = osbuild.KickstartPathOSBuild
img.Kickstart.Language, img.Kickstart.Keyboard = customizations.GetPrimaryLocale()
// ignore ntp servers - we don't currently support setting these in the
// kickstart though kickstart does support setting them
img.Timezone, _ = customizations.GetTimezoneSettings()
if instCust := customizations.GetInstaller(); instCust != nil {
img.NoPasswd = instCust.SudoNopasswd
img.UnattendedKickstart = instCust.Unattended
}
img.Kickstart.Timezone, _ = customizations.GetTimezoneSettings()
img.SquashfsCompression = "xz"
@ -512,7 +514,7 @@ func EdgeInstallerImage(workload workload.Workload,
img.AdditionalDrivers = installerConfig.AdditionalDrivers
}
if len(img.Users)+len(img.Groups) > 0 {
if len(img.Kickstart.Users)+len(img.Kickstart.Groups) > 0 {
// only enable the users module if needed
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
}
@ -524,7 +526,6 @@ func EdgeInstallerImage(workload workload.Workload,
img.Product = t.Arch().Distro().Product()
img.Variant = "edge"
img.OSName = "rhel-edge"
img.OSVersion = t.Arch().Distro().OsVersion()
img.Release = fmt.Sprintf("%s %s", t.Arch().Distro().Product(), t.Arch().Distro().OsVersion())
img.FIPS = customizations.GetFIPS()
@ -676,8 +677,14 @@ func ImageInstallerImage(workload workload.Workload,
}
img.ExtraBasePackages = packageSets[InstallerPkgsKey]
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())
img.Kickstart, err = kickstart.New(customizations)
if err != nil {
return nil, err
}
img.Kickstart.Language = &img.OSCustomizations.Language
img.Kickstart.Keyboard = img.OSCustomizations.Keyboard
img.Kickstart.Timezone = &img.OSCustomizations.Timezone
installerConfig, err := t.getDefaultInstallerConfig()
if err != nil {
@ -691,11 +698,6 @@ func ImageInstallerImage(workload workload.Workload,
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
if instCust := customizations.GetInstaller(); instCust != nil {
img.NoPasswd = instCust.SudoNopasswd
img.UnattendedKickstart = instCust.Unattended
}
img.SquashfsCompression = "xz"
// put the kickstart file in the root of the iso

View file

@ -173,7 +173,7 @@ func ec2CommonPackageSet(t *rhel.ImageType) rpmmd.PackageSet {
"chrony",
"cloud-init",
"cloud-utils-growpart",
"dhcp-client",
"dhcpcd",
"yum-utils",
"dracut-config-generic",
"gdisk",

View file

@ -81,7 +81,11 @@ func checkOptions(t *rhel.ImageType, bp *blueprint.Blueprint, options distro.Ima
warnings = append(warnings, w)
}
if customizations.GetInstaller() != nil {
instCust, err := customizations.GetInstaller()
if err != nil {
return warnings, err
}
if instCust != nil {
// only supported by the Anaconda installer
if slices.Index([]string{"image-installer", "edge-installer", "live-installer"}, t.Name()) == -1 {
return warnings, fmt.Errorf("installer customizations are not supported for %q", t.Name())

View file

@ -175,11 +175,22 @@ func checkOptions(t *rhel.ImageType, bp *blueprint.Blueprint, options distro.Ima
warnings = append(warnings, w)
}
if customizations.GetInstaller() != nil {
instCust, err := customizations.GetInstaller()
if err != nil {
return warnings, err
}
if instCust != nil {
// only supported by the Anaconda installer
if slices.Index([]string{"image-installer", "edge-installer", "live-installer"}, t.Name()) == -1 {
return warnings, fmt.Errorf("installer customizations are not supported for %q", t.Name())
}
if t.Name() == "edge-installer" &&
instCust.Kickstart != nil &&
len(instCust.Kickstart.Contents) > 0 &&
(customizations.GetUsers() != nil || customizations.GetGroups() != nil) {
return warnings, fmt.Errorf("edge-installer installer.kickstart.contents are not supported in combination with users or groups")
}
}
return warnings, nil

View file

@ -194,11 +194,22 @@ func checkOptions(t *rhel.ImageType, bp *blueprint.Blueprint, options distro.Ima
warnings = append(warnings, w)
}
if customizations.GetInstaller() != nil {
instCust, err := customizations.GetInstaller()
if err != nil {
return warnings, err
}
if instCust != nil {
// only supported by the Anaconda installer
if slices.Index([]string{"image-installer", "edge-installer", "live-installer"}, t.Name()) == -1 {
return warnings, fmt.Errorf("installer customizations are not supported for %q", t.Name())
}
if t.Name() == "edge-installer" &&
instCust.Kickstart != nil &&
len(instCust.Kickstart.Contents) > 0 &&
(customizations.GetUsers() != nil || customizations.GetGroups() != nil) {
return warnings, fmt.Errorf("edge-installer installer.kickstart.contents are not supported in combination with users or groups")
}
}
return warnings, nil

View file

@ -8,7 +8,7 @@ import (
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/platform"
@ -20,15 +20,12 @@ type AnacondaContainerInstaller struct {
Base
Platform platform.Platform
ExtraBasePackages rpmmd.PackageSet
Users []users.User
Groups []users.Group
SquashfsCompression string
ISOLabel string
Product string
Variant string
OSName string
Ref string
OSVersion string
Release string
@ -43,11 +40,9 @@ type AnacondaContainerInstaller struct {
AdditionalDrivers []string
FIPS bool
// Kernel options that will be apended to the installed system
// (not the iso)
KickstartKernelOptionsAppend []string
// Enable networking on on boot in the installed system
KickstartNetworkOnBoot bool
Kickstart *kickstart.Options
UseRHELLoraxTemplates bool
}
func NewAnacondaContainerInstaller(container container.SourceSpec, ref string) *AnacondaContainerInstaller {
@ -76,14 +71,16 @@ func (img *AnacondaContainerInstaller) InstantiateManifest(m *manifest.Manifest,
img.Preview,
)
// This is only built with ELN for now
anacondaPipeline.UseRHELLoraxTemplates = true
anacondaPipeline.UseRHELLoraxTemplates = img.UseRHELLoraxTemplates
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
anacondaPipeline.ExcludePackages = img.ExtraBasePackages.Exclude
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
anacondaPipeline.Users = img.Users
anacondaPipeline.Groups = img.Groups
anacondaPipeline.InteractiveDefaultsKickstart = &kickstart.Options{
Users: img.Kickstart.Users,
Groups: img.Kickstart.Groups,
Path: osbuild.KickstartPathOSBuild,
}
anacondaPipeline.Variant = img.Variant
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == arch.ARCH_X86_64)
anacondaPipeline.Checkpoint()
@ -105,8 +102,11 @@ func (img *AnacondaContainerInstaller) InstantiateManifest(m *manifest.Manifest,
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
bootTreePipeline.ISOLabel = img.ISOLabel
kspath := osbuild.KickstartPathOSBuild
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, kspath)}
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath)}
if img.FIPS {
bootTreePipeline.KernelOpts = append(bootTreePipeline.KernelOpts, "fips=1")
}
@ -117,17 +117,11 @@ func (img *AnacondaContainerInstaller) InstantiateManifest(m *manifest.Manifest,
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(buildPipeline, anacondaPipeline, rootfsImagePipeline, bootTreePipeline)
isoTreePipeline.PartitionTable = efiBootPartitionTable(rng)
isoTreePipeline.Release = img.Release
isoTreePipeline.OSName = img.OSName
isoTreePipeline.Users = img.Users
isoTreePipeline.Groups = img.Groups
isoTreePipeline.KickstartKernelOptionsAppend = img.KickstartKernelOptionsAppend
isoTreePipeline.KickstartNetworkOnBoot = img.KickstartNetworkOnBoot
isoTreePipeline.Kickstart = img.Kickstart
isoTreePipeline.SquashfsCompression = img.SquashfsCompression
// For ostree installers, always put the kickstart file in the root of the ISO
isoTreePipeline.KSPath = kspath
isoTreePipeline.PayloadPath = "/container"
isoTreePipeline.ContainerSource = &img.ContainerSource

View file

@ -26,7 +26,6 @@ type AnacondaLiveInstaller struct {
ISOLabel string
Product string
Variant string
OSName string
OSVersion string
Release string
Preview bool
@ -93,7 +92,6 @@ func (img *AnacondaLiveInstaller) InstantiateManifest(m *manifest.Manifest,
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(buildPipeline, livePipeline, rootfsImagePipeline, bootTreePipeline)
isoTreePipeline.PartitionTable = efiBootPartitionTable(rng)
isoTreePipeline.Release = img.Release
isoTreePipeline.OSName = img.OSName
isoTreePipeline.KernelOpts = kernelOpts
isoTreePipeline.ISOLinux = isoLinuxEnabled

View file

@ -7,7 +7,7 @@ import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
@ -20,30 +20,17 @@ type AnacondaOSTreeInstaller struct {
Base
Platform platform.Platform
ExtraBasePackages rpmmd.PackageSet
Users []users.User
Groups []users.Group
Language *string
Keyboard *string
Timezone *string
// Create a sudoers drop-in file for each user or group to enable the
// NOPASSWD option
NoPasswd []string
// Add kickstart options to make the installation fully unattended
UnattendedKickstart bool
Kickstart *kickstart.Options
SquashfsCompression string
ISOLabel string
Product string
Variant string
OSName string
OSVersion string
Release string
Preview bool
Remote string
Commit ostree.SourceSpec
@ -82,8 +69,12 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
anacondaPipeline.ExcludePackages = img.ExtraBasePackages.Exclude
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
anacondaPipeline.Users = img.Users
anacondaPipeline.Groups = img.Groups
if img.Kickstart != nil {
anacondaPipeline.InteractiveDefaultsKickstart = &kickstart.Options{
Users: img.Kickstart.Users,
Groups: img.Kickstart.Groups,
}
}
anacondaPipeline.Variant = img.Variant
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == arch.ARCH_X86_64)
anacondaPipeline.Checkpoint()
@ -105,8 +96,11 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
bootTreePipeline.ISOLabel = img.ISOLabel
kspath := osbuild.KickstartPathOSBuild
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, kspath)}
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath)}
if img.FIPS {
bootTreePipeline.KernelOpts = append(bootTreePipeline.KernelOpts, "fips=1")
}
@ -117,19 +111,9 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(buildPipeline, anacondaPipeline, rootfsImagePipeline, bootTreePipeline)
isoTreePipeline.PartitionTable = efiBootPartitionTable(rng)
isoTreePipeline.Release = img.Release
isoTreePipeline.OSName = img.OSName
isoTreePipeline.Remote = img.Remote
isoTreePipeline.Users = img.Users
isoTreePipeline.Groups = img.Groups
isoTreePipeline.NoPasswd = img.NoPasswd
isoTreePipeline.UnattendedKickstart = img.UnattendedKickstart
isoTreePipeline.Kickstart = img.Kickstart
isoTreePipeline.SquashfsCompression = img.SquashfsCompression
isoTreePipeline.Language = img.Language
isoTreePipeline.Keyboard = img.Keyboard
isoTreePipeline.Timezone = img.Timezone
// For ostree installers, always put the kickstart file in the root of the ISO
isoTreePipeline.KSPath = kspath
isoTreePipeline.PayloadPath = "/ostree/repo"
isoTreePipeline.OSTreeCommitSource = &img.Commit

View file

@ -10,7 +10,7 @@ import (
"github.com/osbuild/images/internal/workload"
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
@ -45,8 +45,6 @@ type AnacondaTarInstaller struct {
Workload workload.Workload
ExtraBasePackages rpmmd.PackageSet
Users []users.User
Groups []users.Group
// If set, the kickstart file will be added to the bootiso-tree at the
// default path for osbuild, otherwise any kickstart options will be
@ -55,22 +53,13 @@ type AnacondaTarInstaller struct {
// because automatic installations cannot be configured using interactive
// defaults.
ISORootKickstart bool
// Create a sudoers drop-in file for each user or group to enable the
// NOPASSWD option
NoPasswd []string
// Add kickstart options to make the installation fully unattended.
// Enabling this option also automatically enables the ISORootKickstart
// option.
UnattendedKickstart bool
Kickstart *kickstart.Options
SquashfsCompression string
ISOLabel string
Product string
Variant string
OSName string
OSVersion string
Release string
Preview bool
@ -96,7 +85,7 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
buildPipeline := manifest.NewBuild(m, runner, repos, nil)
buildPipeline.Checkpoint()
if img.UnattendedKickstart {
if img.Kickstart != nil && img.Kickstart.Unattended {
// if we're building an unattended installer, override the
// ISORootKickstart option
img.ISORootKickstart = true
@ -116,8 +105,13 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
anacondaPipeline.ExcludePackages = img.ExtraBasePackages.Exclude
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
anacondaPipeline.Users = img.Users
anacondaPipeline.Groups = img.Groups
if img.Kickstart != nil {
anacondaPipeline.InteractiveDefaultsKickstart = &kickstart.Options{
Users: img.Kickstart.Users,
Groups: img.Kickstart.Groups,
Path: osbuild.KickstartPathOSBuild,
}
}
anacondaPipeline.Variant = img.Variant
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == arch.ARCH_X86_64)
anacondaPipeline.AdditionalAnacondaModules = img.AdditionalAnacondaModules
@ -150,7 +144,11 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
kspath := osbuild.KickstartPathOSBuild
kernelOpts := []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel)}
if img.ISORootKickstart {
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, kspath))
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
}
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath))
}
if img.OSCustomizations.FIPS {
kernelOpts = append(kernelOpts, "fips=1")
@ -170,24 +168,12 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
// TODO: the partition table is required - make it a ctor arg or set a default one in the pipeline
isoTreePipeline.PartitionTable = efiBootPartitionTable(rng)
isoTreePipeline.Release = img.Release
isoTreePipeline.OSName = img.OSName
isoTreePipeline.Users = img.Users
isoTreePipeline.Groups = img.Groups
isoTreePipeline.Keyboard = img.OSCustomizations.Keyboard
if img.OSCustomizations.Language != "" {
isoTreePipeline.Language = &img.OSCustomizations.Language
}
if img.OSCustomizations.Timezone != "" {
isoTreePipeline.Timezone = &img.OSCustomizations.Timezone
}
isoTreePipeline.Kickstart = img.Kickstart
isoTreePipeline.PayloadPath = tarPath
if img.ISORootKickstart {
isoTreePipeline.KSPath = kspath
isoTreePipeline.Kickstart.Path = kspath
}
isoTreePipeline.NoPasswd = img.NoPasswd
isoTreePipeline.UnattendedKickstart = img.UnattendedKickstart
isoTreePipeline.SquashfsCompression = img.SquashfsCompression
isoTreePipeline.OSPipeline = osPipeline

View file

@ -6,6 +6,7 @@ import (
"github.com/osbuild/images/internal/environment"
"github.com/osbuild/images/internal/workload"
"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/customizations/bootc"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/platform"
@ -33,6 +34,11 @@ type OSTreeArchive struct {
InstallWeakDeps bool
BootContainer bool
// bootc install config for defining the preferred filesystem type and
// kernel arguments for bootable containers.
// This is ignored if BootContainer = false.
BootcConfig *bootc.Config
}
func NewOSTreeArchive(ref string) *OSTreeArchive {
@ -64,6 +70,7 @@ func (img *OSTreeArchive) InstantiateManifest(m *manifest.Manifest,
var artifact *artifact.Artifact
if img.BootContainer {
osPipeline.Bootupd = true
osPipeline.BootcConfig = img.BootcConfig
encapsulatePipeline := manifest.NewOSTreeEncapsulate(buildPipeline, ostreeCommitPipeline, "ostree-encapsulate")
encapsulatePipeline.SetFilename(img.Filename)
artifact = encapsulatePipeline.Export()

View file

@ -8,6 +8,7 @@ import (
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
@ -40,11 +41,6 @@ type AnacondaInstaller struct {
// Extra repositories to install packages from
ExtraRepos []rpmmd.RepoConfig
// Users and Groups to create during installation.
// If empty, then the user can interactively create users at install time.
Users []users.User
Groups []users.Group
// Biosdevname indicates whether or not biosdevname should be used to
// name network devices when booting the installer. This may affect
// the naming of network devices on the target system.
@ -66,6 +62,11 @@ type AnacondaInstaller struct {
// will be written to /usr/share/anaconda/interactive-defaults
InteractiveDefaults *AnacondaInteractiveDefaults
// Kickstart options that will be written to the interactive defaults
// kickstart file. Currently only supports Users and Groups. Other
// properties are ignored.
InteractiveDefaultsKickstart *kickstart.Options
// Additional anaconda modules to enable
AdditionalAnacondaModules []string
@ -220,7 +221,7 @@ func (p *AnacondaInstaller) serialize() osbuild.Pipeline {
// being serialized
switch p.Type {
case AnacondaInstallerTypeLive:
if len(p.Users) != 0 || len(p.Groups) != 0 {
if p.InteractiveDefaultsKickstart != nil && (len(p.InteractiveDefaultsKickstart.Users) != 0 || len(p.InteractiveDefaultsKickstart.Groups) != 0) {
panic("anaconda installer type live does not support users and groups customization")
}
if p.InteractiveDefaults != nil {
@ -293,10 +294,16 @@ func (p *AnacondaInstaller) payloadStages() []*osbuild.Stage {
stages = append(stages, osbuild.NewSELinuxConfigStage(&osbuild.SELinuxConfigStageOptions{State: osbuild.SELinuxStatePermissive}))
if p.InteractiveDefaults != nil {
var ksUsers []users.User
var ksGroups []users.Group
if p.InteractiveDefaultsKickstart != nil {
ksUsers = p.InteractiveDefaultsKickstart.Users
ksGroups = p.InteractiveDefaultsKickstart.Groups
}
kickstartOptions, err := osbuild.NewKickstartStageOptionsWithLiveIMG(
osbuild.KickstartPathInteractiveDefaults,
p.Users,
p.Groups,
ksUsers,
ksGroups,
p.InteractiveDefaults.TarPath,
)

View file

@ -9,7 +9,7 @@ import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
@ -24,28 +24,7 @@ type AnacondaInstallerISOTree struct {
Base
// TODO: review optional and mandatory fields and their meaning
OSName string
Release string
Remote string
Users []users.User
Groups []users.Group
Language *string
Keyboard *string
Timezone *string
// Kernel options that will be apended to the installed system
// (not the iso)
KickstartKernelOptionsAppend []string
// Enable networking on on boot in the installed system
KickstartNetworkOnBoot bool
// Create a sudoers drop-in file for each user or group to enable the
// NOPASSWD option
NoPasswd []string
// Add kickstart options to make the installation fully unattended
UnattendedKickstart bool
PartitionTable *disk.PartitionTable
@ -53,12 +32,6 @@ type AnacondaInstallerISOTree struct {
rootfsPipeline *ISORootfsImg
bootTreePipeline *EFIBootTree
// The location of the kickstart file, if it will be added to the
// bootiso-tree.
// Otherwise, it should be defined in the interactive defaults of the
// Anaconda pipeline.
KSPath string
// The path where the payload (tarball, ostree repo, or container) will be stored.
PayloadPath string
@ -79,6 +52,8 @@ type AnacondaInstallerISOTree struct {
// Enable ISOLinux stage
ISOLinux bool
Kickstart *kickstart.Options
Files []*fsnode.File
}
@ -223,8 +198,8 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline {
if p.anacondaPipeline.Type == AnacondaInstallerTypePayload {
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.stage2=hd:LABEL=%s", p.isoLabel))
if p.KSPath != "" {
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", p.isoLabel, p.KSPath))
if p.Kickstart != nil && p.Kickstart.Path != "" {
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", p.isoLabel, p.Kickstart.Path))
}
}
@ -320,8 +295,7 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline {
Size: fmt.Sprintf("%d", p.PartitionTable.Size),
}))
efibootDevice := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: filename})
for _, stage := range osbuild.GenMkfsStages(p.PartitionTable, efibootDevice) {
for _, stage := range osbuild.GenMkfsStages(p.PartitionTable, filename) {
pipeline.AddStage(stage)
}
@ -377,15 +351,22 @@ func (p *AnacondaInstallerISOTree) ostreeCommitStages() []*osbuild.Stage {
osbuild.NewOstreePullStageInputs("org.osbuild.source", p.ostreeCommitSpec.Checksum, p.ostreeCommitSpec.Ref),
))
if p.Kickstart == nil {
panic(fmt.Sprintf("Kickstart options not set for %s pipeline", p.name))
}
if p.Kickstart.OSTree == nil {
panic(fmt.Sprintf("Kickstart ostree options not set for %s pipeline", p.name))
}
// Configure the kickstart file with the payload and any user options
kickstartOptions, err := osbuild.NewKickstartStageOptionsWithOSTreeCommit(
p.KSPath,
p.Users,
p.Groups,
p.Kickstart.Path,
p.Kickstart.Users,
p.Kickstart.Groups,
makeISORootPath(p.PayloadPath),
p.ostreeCommitSpec.Ref,
p.Remote,
p.OSName)
p.Kickstart.OSTree.Remote,
p.Kickstart.OSTree.OSName)
if err != nil {
panic(fmt.Sprintf("failed to create kickstart stage options: %v", err))
@ -415,11 +396,24 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
image,
nil))
stages = append(stages, p.bootcInstallerKickstartStages()...)
return stages
}
// bootcInstallerKickstartStages sets up kickstart-related stages for Anaconda
// ISOs that install a bootc bootable container.
func (p *AnacondaInstallerISOTree) bootcInstallerKickstartStages() []*osbuild.Stage {
if p.Kickstart == nil {
panic(fmt.Sprintf("Kickstart options not set for %s pipeline", p.name))
}
stages := make([]*osbuild.Stage, 0)
// do what we can in our kickstart stage
kickstartOptions, err := osbuild.NewKickstartStageOptionsWithOSTreeContainer(
p.KSPath,
p.Users,
p.Groups,
p.Kickstart.Path,
p.Kickstart.Users,
p.Kickstart.Groups,
path.Join("/run/install/repo", p.PayloadPath),
"oci",
"",
@ -428,6 +422,28 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
panic(fmt.Sprintf("failed to create kickstart stage options: %v", err))
}
// kickstart.New() already validates the options but they may have been
// modified since then, so validate them before we create the stages
if err := p.Kickstart.Validate(); err != nil {
panic(err)
}
if p.Kickstart.UserFile != nil {
// when a user defines their own kickstart, we create a kickstart that
// takes care of the installation and let the user kickstart handle
// everything else
stages = append(stages, osbuild.NewKickstartStage(kickstartOptions))
kickstartFile, err := kickstartOptions.IncludeRaw(p.Kickstart.UserFile.Contents)
if err != nil {
panic(err)
}
p.Files = []*fsnode.File{kickstartFile}
return append(stages, osbuild.GenFileNodesStages(p.Files)...)
}
// create a fully unattended/automated kickstart
// NOTE: these are similar to the unattended kickstart options in the
// other two payload configurations but partitioning is different and
// we need to add that separately, so we can't use makeKickstartStage
@ -438,13 +454,16 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
// NOTE: These were decided somewhat arbitrarily for the BIB installer. We
// might want to drop them here and move them into the bib code as
// project-specific defaults.
// TODO: unify with other ostree variants and allow overrides from customizations
kickstartOptions.Lang = "en_US.UTF-8"
kickstartOptions.Keyboard = "us"
kickstartOptions.Timezone = "UTC"
kickstartOptions.ClearPart = &osbuild.ClearPartOptions{
All: true,
}
if len(p.KickstartKernelOptionsAppend) > 0 {
if len(p.Kickstart.KernelOptionsAppend) > 0 {
kickstartOptions.Bootloader = &osbuild.BootloaderOptions{
// We currently leaves quoting to the
// user. This is generally ok - to do better
@ -452,10 +471,10 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
// parser, see
// https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
// and lib/cmdline.c in the kernel source
Append: strings.Join(p.KickstartKernelOptionsAppend, " "),
Append: strings.Join(p.Kickstart.KernelOptionsAppend, " "),
}
}
if p.KickstartNetworkOnBoot {
if p.Kickstart.NetworkOnBoot {
kickstartOptions.Network = []osbuild.NetworkOptions{
{BootProto: "dhcp", Device: "link", Activate: common.ToPtr(true), OnBoot: "on"},
}
@ -491,8 +510,7 @@ bootc switch --mutate-in-place --transport %s %s
p.Files = []*fsnode.File{kickstartFile}
stages = append(stages, osbuild.GenFileNodesStages(p.Files)...)
return stages
return append(stages, osbuild.GenFileNodesStages(p.Files)...)
}
func (p *AnacondaInstallerISOTree) tarPayloadStages() []*osbuild.Stage {
@ -503,11 +521,11 @@ func (p *AnacondaInstallerISOTree) tarPayloadStages() []*osbuild.Stage {
// If the KSPath is set, we need to add the kickstart stage to this (bootiso-tree) pipeline.
// If it's not specified here, it should have been added to the InteractiveDefaults in the anaconda-tree.
if p.KSPath != "" {
if p.Kickstart != nil && p.Kickstart.Path != "" {
kickstartOptions, err := osbuild.NewKickstartStageOptionsWithLiveIMG(
p.KSPath,
p.Users,
p.Groups,
p.Kickstart.Path,
p.Kickstart.Users,
p.Kickstart.Groups,
makeISORootPath(p.PayloadPath))
if err != nil {
@ -522,47 +540,73 @@ func (p *AnacondaInstallerISOTree) tarPayloadStages() []*osbuild.Stage {
// Create the base kickstart stage with any options required for unattended
// installation if set and with any extra file insertion stage required for
// extra kickstart content.
func (p *AnacondaInstallerISOTree) makeKickstartStages(kickstartOptions *osbuild.KickstartStageOptions) []*osbuild.Stage {
func (p *AnacondaInstallerISOTree) makeKickstartStages(stageOptions *osbuild.KickstartStageOptions) []*osbuild.Stage {
kickstartOptions := p.Kickstart
if kickstartOptions == nil {
kickstartOptions = new(kickstart.Options)
}
stages := make([]*osbuild.Stage, 0)
if p.UnattendedKickstart {
// kickstart.New() already validates the options but they may have been
// modified since then, so validate them before we create the stages
if err := p.Kickstart.Validate(); err != nil {
panic(err)
}
if kickstartOptions.UserFile != nil {
stages = append(stages, osbuild.NewKickstartStage(stageOptions))
if kickstartOptions.UserFile != nil {
kickstartFile, err := stageOptions.IncludeRaw(kickstartOptions.UserFile.Contents)
if err != nil {
panic(err)
}
p.Files = []*fsnode.File{kickstartFile}
stages = append(stages, osbuild.GenFileNodesStages(p.Files)...)
}
}
if kickstartOptions.Unattended {
// set the default options for Unattended kickstart
kickstartOptions.DisplayMode = "text"
stageOptions.DisplayMode = "text"
// override options that can be configured by the image type or the user
kickstartOptions.Lang = "en_US.UTF-8"
if p.Language != nil {
kickstartOptions.Lang = *p.Language
stageOptions.Lang = "en_US.UTF-8"
if kickstartOptions.Language != nil {
stageOptions.Lang = *kickstartOptions.Language
}
kickstartOptions.Keyboard = "us"
if p.Keyboard != nil {
kickstartOptions.Keyboard = *p.Keyboard
stageOptions.Keyboard = "us"
if kickstartOptions.Keyboard != nil {
stageOptions.Keyboard = *kickstartOptions.Keyboard
}
kickstartOptions.Timezone = "UTC"
if p.Timezone != nil {
kickstartOptions.Timezone = *p.Timezone
stageOptions.Timezone = "UTC"
if kickstartOptions.Timezone != nil {
stageOptions.Timezone = *kickstartOptions.Timezone
}
kickstartOptions.Reboot = &osbuild.RebootOptions{Eject: true}
kickstartOptions.RootPassword = &osbuild.RootPasswordOptions{Lock: true}
stageOptions.Reboot = &osbuild.RebootOptions{Eject: true}
stageOptions.RootPassword = &osbuild.RootPasswordOptions{Lock: true}
kickstartOptions.ZeroMBR = true
kickstartOptions.ClearPart = &osbuild.ClearPartOptions{All: true, InitLabel: true}
kickstartOptions.AutoPart = &osbuild.AutoPartOptions{Type: "plain", FSType: "xfs", NoHome: true}
stageOptions.ZeroMBR = true
stageOptions.ClearPart = &osbuild.ClearPartOptions{All: true, InitLabel: true}
stageOptions.AutoPart = &osbuild.AutoPartOptions{Type: "plain", FSType: "xfs", NoHome: true}
kickstartOptions.Network = []osbuild.NetworkOptions{
stageOptions.Network = []osbuild.NetworkOptions{
{BootProto: "dhcp", Device: "link", Activate: common.ToPtr(true), OnBoot: "on"},
}
}
stages = append(stages, osbuild.NewKickstartStage(kickstartOptions))
stages = append(stages, osbuild.NewKickstartStage(stageOptions))
hardcodedKickstartBits := makeKickstartSudoersPost(p.NoPasswd)
hardcodedKickstartBits := makeKickstartSudoersPost(kickstartOptions.SudoNopasswd)
if hardcodedKickstartBits != "" {
// Because osbuild core only supports a subset of options,
// we append to the base here with hardcoded wheel group with NOPASSWD option
kickstartFile, err := kickstartOptions.IncludeRaw(hardcodedKickstartBits)
kickstartFile, err := stageOptions.IncludeRaw(hardcodedKickstartBits)
if err != nil {
panic(err)
}

View file

@ -111,8 +111,7 @@ func (p *CoreOSISOTree) serialize() osbuild.Pipeline {
Size: fmt.Sprintf("%d", p.PartitionTable.Size),
}))
efibootDevice := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: filename})
for _, stage := range osbuild.GenMkfsStages(p.PartitionTable, efibootDevice) {
for _, stage := range osbuild.GenMkfsStages(p.PartitionTable, filename) {
pipeline.AddStage(stage)
}

View file

@ -10,6 +10,7 @@ import (
"github.com/osbuild/images/internal/workload"
"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/bootc"
"github.com/osbuild/images/pkg/customizations/fsnode"
"github.com/osbuild/images/pkg/customizations/shell"
"github.com/osbuild/images/pkg/customizations/users"
@ -163,6 +164,9 @@ type OS struct {
// payload. Only works with ostree-based images.
Bootupd bool
// Add a bootc config file to the image (for bootable containers)
BootcConfig *bootc.Config
// Partition table, if nil the tree cannot be put on a partitioned disk
PartitionTable *disk.PartitionTable
@ -311,6 +315,16 @@ func (p *OS) getBuildPackages(distro Distro) []string {
packages = append(packages, "openscap-utils")
}
if p.BootcConfig != nil {
switch distro {
case DISTRO_EL8:
packages = append(packages, "python3-pytoml")
case DISTRO_EL10:
default:
packages = append(packages, "python3-toml")
}
}
return packages
}
@ -819,10 +833,18 @@ func (p *OS) serialize() osbuild.Pipeline {
if p.Bootupd {
pipeline.AddStage(osbuild.NewBootupdGenMetadataStage())
}
if cfg := p.BootcConfig; cfg != nil {
pipeline.AddStage(osbuild.NewBootcInstallConfigStage(
osbuild.GenBootcInstallOptions(cfg.Filename, cfg.RootFilesystemType),
))
}
} else {
if p.Bootupd {
panic("bootupd is only compatible with ostree-based images, this is a programming error")
}
if p.BootcConfig != nil {
panic("bootc config is only compatible with ostree-based images, this is a programming error")
}
}
return pipeline

View file

@ -0,0 +1,51 @@
package osbuild
type BootcInstallConfigStageOptions struct {
Filename string `json:"filename"`
Config BootcInstallConfig `json:"config"`
}
func (BootcInstallConfigStageOptions) isStageOptions() {}
type BootcInstallConfig struct {
Install *BootcInstallConfigInstall `json:"install,omitempty"`
KernelArgs []string `json:"kargs,omitempty"`
Block []string `json:"block,omitempty"`
}
type BootcInstallConfigInstall struct {
Filesystem BootcInstallConfigFilesystem `json:"filesystem"`
}
type BootcInstallConfigFilesystem struct {
Root BootcInstallConfigFilesystemRoot `json:"root"`
}
type BootcInstallConfigFilesystemRoot struct {
Type string `json:"type"`
}
// GenBootcInstallOptions is a helper function for creating stage options for
// org.osbuild.bootc.install.config with just the filename and root filesystem
// type set.
func GenBootcInstallOptions(filename, rootType string) *BootcInstallConfigStageOptions {
return &BootcInstallConfigStageOptions{
Filename: filename,
Config: BootcInstallConfig{
Install: &BootcInstallConfigInstall{
Filesystem: BootcInstallConfigFilesystem{
Root: BootcInstallConfigFilesystemRoot{
Type: rootType,
},
},
},
},
}
}
func NewBootcInstallConfigStage(options *BootcInstallConfigStageOptions) *Stage {
return &Stage{
Type: "org.osbuild.bootc.install.config",
Options: options,
}
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/disk"
)
@ -85,20 +86,19 @@ func genMountsForBootupd(source string, pt *disk.PartitionTable) ([]Mount, error
if part.Payload == nil {
continue
}
// TODO: support things like LVM here via supporting "disk.Container"
mnt, ok := part.Payload.(disk.Mountable)
if !ok {
return nil, fmt.Errorf("type %v not supported by bootupd handling yet", mnt)
}
partNum := idx + 1
name := fmt.Sprintf("part%v", partNum)
mount, err := genOsbuildMount(name, source, mnt)
if err != nil {
return nil, err
// TODO: support things like LVM here via supporting "disk.Container"
switch payload := part.Payload.(type) {
case disk.Mountable:
mount, err := genOsbuildMount(source, payload)
if err != nil {
return nil, err
}
mount.Partition = common.ToPtr(idx + 1)
mounts = append(mounts, *mount)
default:
return nil, fmt.Errorf("type %T not supported by bootupd handling yet", part.Payload)
}
mount.Partition = &partNum
mounts = append(mounts, *mount)
}
// this must be sorted in so that mounts do not shadow each other
sort.Slice(mounts, func(i, j int) bool {

View file

@ -164,6 +164,18 @@ func deviceName(p disk.Entity) string {
panic(fmt.Sprintf("unsupported device type in deviceName: '%T'", p))
}
// getDevices takes an entity path, and returns osbuild devices required before being able to mount the leaf Mountable
//
// - path is an entity path as defined by the disk.entityPath function
// - filename is the name of an underlying image file (which will get loop-mounted)
// - lockLoopback determines whether the loop device will get locked after creation
//
// The device names are created from the payload that they are holding. This is useful to easily visually map e.g.
// a loopback device and its mount (in the case of ordinary partitions): they should have the same, or similar name.
//
// The first returned value is a map of devices for the given path.
// The second returned value is the name of the last device in the path. This is the device that should be used as the
// source for the mount.
func getDevices(path []disk.Entity, filename string, lockLoopback bool) (map[string]Device, string) {
var pt *disk.PartitionTable
@ -225,8 +237,14 @@ func pathEscape(path string) string {
return strings.ReplaceAll(path, "/", "-")
}
func genOsbuildMount(name, source string, mnt disk.Mountable) (*Mount, error) {
// genOsbuildMount generates an osbuild mount from Mountable mnt
//
// - source is the name of the device that the mount should be created from.
// The name of the mount is derived from the mountpoint of the mountable, escaped with pathEscape. This shouldn't
// create any conflicts, as the mountpoint is unique within the partition table.
func genOsbuildMount(source string, mnt disk.Mountable) (*Mount, error) {
mountpoint := mnt.GetMountpoint()
name := pathEscape(mountpoint)
t := mnt.GetFSType()
switch t {
case "xfs":
@ -242,21 +260,30 @@ func genOsbuildMount(name, source string, mnt disk.Mountable) (*Mount, error) {
}
}
// genMountsDevicesFromPt generates osbuild mounts and devices from a disk.PartitionTable
// filename is the name of the underlying image file (which will get loop-mounted).
//
// Returned values:
// 1) the name of the mount for the filesystem root
// 2) generated mounts
// 3) generated devices
// 4) error if any
func genMountsDevicesFromPt(filename string, pt *disk.PartitionTable) (string, []Mount, map[string]Device, error) {
devices := make(map[string]Device, len(pt.Partitions))
mounts := make([]Mount, 0, len(pt.Partitions))
var fsRootMntName string
genMounts := func(mnt disk.Mountable, path []disk.Entity) error {
stageDevices, name := getDevices(path, filename, false)
mountpoint := mnt.GetMountpoint()
if mountpoint == "/" {
fsRootMntName = name
}
mount, err := genOsbuildMount(name, name, mnt)
stageDevices, leafDeviceName := getDevices(path, filename, false)
mount, err := genOsbuildMount(leafDeviceName, mnt)
if err != nil {
return err
}
mountpoint := mnt.GetMountpoint()
if mountpoint == "/" {
fsRootMntName = mount.Name
}
mounts = append(mounts, *mount)
// update devices map with new elements from stageDevices

View file

@ -99,7 +99,7 @@ func GenImagePrepareStages(pt *disk.PartitionTable, filename string, partTool Pa
stages = append(stages, s...)
// Generate all the filesystems on partitons and devices
s = GenMkfsStages(pt, loopback)
s = GenMkfsStages(pt, filename)
stages = append(stages, s...)
return stages

View file

@ -243,6 +243,10 @@ func NewKickstartStageOptionsWithLiveIMG(
// generated in place of the original file and is returned as an fsnode.File.
// The raw content *should not* contain the %include statement.
func (options *KickstartStageOptions) IncludeRaw(raw string) (*fsnode.File, error) {
// TODO: flip the way this function includes one kickstart in another when
// we add an include option to the kickstart stage so that the raw part
// remains intact, our own kickstart file remains the "primary", and we
// %include the raw kickstart from our own.
origPath := options.Path
origName := filepath.Base(origPath)

View file

@ -8,23 +8,18 @@ import (
// GenMkfsStages generates a list of org.mkfs.* stages based on a
// partition table description for a single device node
func GenMkfsStages(pt *disk.PartitionTable, device *Device) []*Stage {
// filename is the path to the underlying image file (to be used as a source for the loopback device)
func GenMkfsStages(pt *disk.PartitionTable, filename string) []*Stage {
stages := make([]*Stage, 0, len(pt.Partitions))
// assume loopback device for simplicity since it's the only one currently supported
// panic if the conversion fails
devOptions, ok := device.Options.(*LoopbackDeviceOptions)
if !ok {
panic("GenMkfsStages: failed to convert device options to loopback options")
}
genStage := func(mnt disk.Mountable, path []disk.Entity) error {
t := mnt.GetFSType()
var stage *Stage
stageDevices, lastName := getDevices(path, devOptions.Filename, true)
stageDevices, lastName := getDevices(path, filename, true)
// the last device on the PartitionTable must be named "device"
// The last device in the chain must be named "device", because that's the device that mkfs stages run on.
// See their schema for reference.
lastDevice := stageDevices[lastName]
delete(stageDevices, lastName)
stageDevices["device"] = lastDevice