pipelines: add fedora OS pipeline

This pulls out the OS pipeline, without changing the parameters. The dependency
between the OS pipeline and build pipeline, is now explicit, rather than by name.
This commit is contained in:
Tom Gundersen 2022-06-25 17:37:13 +01:00
parent b6d6626a5d
commit 562a5b1127
3 changed files with 420 additions and 213 deletions

View file

@ -5,10 +5,8 @@ import (
"math/rand"
"path"
"path/filepath"
"strings"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/disk"
"github.com/osbuild/osbuild-composer/internal/distro"
pipeline "github.com/osbuild/osbuild-composer/internal/distro/pipelines"
@ -29,15 +27,15 @@ func qcow2Pipelines(t *imageType, customizations *blueprint.Customizations, opti
return nil, err
}
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
diskfile := "disk.img"
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name)
imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, partitionTable, t.arch, kernelVer)
imagePipeline := liveImagePipeline(treePipeline.Name(), diskfile, partitionTable, t.arch, kernelVer)
pipelines = append(pipelines, *imagePipeline)
qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, osbuild.QEMUFormatQCOW2, osbuild.QCOW2Options{Compat: "1.1"})
@ -70,15 +68,15 @@ func vhdPipelines(t *imageType, customizations *blueprint.Customizations, option
return nil, err
}
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
diskfile := "disk.img"
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name)
imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, partitionTable, t.arch, kernelVer)
imagePipeline := liveImagePipeline(treePipeline.Name(), diskfile, partitionTable, t.arch, kernelVer)
pipelines = append(pipelines, *imagePipeline)
qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, osbuild.QEMUFormatVPC, nil)
@ -99,15 +97,15 @@ func vmdkPipelines(t *imageType, customizations *blueprint.Customizations, optio
return nil, err
}
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
diskfile := "disk.img"
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name)
imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, partitionTable, t.arch, kernelVer)
imagePipeline := liveImagePipeline(treePipeline.Name(), diskfile, partitionTable, t.arch, kernelVer)
pipelines = append(pipelines, *imagePipeline)
qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, osbuild.QEMUFormatVMDK, osbuild.VMDKOptions{Subformat: osbuild.VMDKSubformatStreamOptimized})
@ -128,15 +126,15 @@ func openstackPipelines(t *imageType, customizations *blueprint.Customizations,
return nil, err
}
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
diskfile := "disk.img"
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name)
imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, partitionTable, t.arch, kernelVer)
imagePipeline := liveImagePipeline(treePipeline.Name(), diskfile, partitionTable, t.arch, kernelVer)
pipelines = append(pipelines, *imagePipeline)
qemuPipeline := qemuPipeline(imagePipeline.Name, diskfile, t.filename, osbuild.QEMUFormatQCOW2, nil)
@ -159,14 +157,14 @@ func ec2CommonPipelines(t *imageType, customizations *blueprint.Customizations,
return nil, err
}
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, partitionTable)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packageSetSpecs[osPkgsKey], customizations.GetKernel().Name)
imagePipeline := liveImagePipeline(treePipeline.Name, diskfile, partitionTable, t.arch, kernelVer)
imagePipeline := liveImagePipeline(treePipeline.Name(), diskfile, partitionTable, t.arch, kernelVer)
pipelines = append(pipelines, *imagePipeline)
return pipelines, nil
}
@ -220,12 +218,12 @@ func iotCorePipelines(t *imageType, customizations *blueprint.Customizations, op
buildPipeline.PackageSpecs = packageSetSpecs[buildPkgsKey]
pipelines = append(pipelines, buildPipeline.Serialize())
treePipeline, err := osPipeline(t, repos, packageSetSpecs[osPkgsKey], customizations, options, nil)
treePipeline, err := osPipeline(&buildPipeline, t, repos, packageSetSpecs[osPkgsKey], customizations, options, nil)
if err != nil {
return nil, err
}
pipelines = append(pipelines, *treePipeline)
pipelines = append(pipelines, treePipeline.Serialize())
pipelines = append(pipelines, *ostreeCommitPipeline(options, t.arch.distro.osVersion))
@ -259,213 +257,117 @@ func iotContainerPipelines(t *imageType, customizations *blueprint.Customization
return pipelines, nil
}
func osPipeline(t *imageType,
func osPipeline(buildPipeline *pipeline.BuildPipeline,
t *imageType,
repos []rpmmd.RepoConfig,
packages []rpmmd.PackageSpec,
c *blueprint.Customizations,
options distro.ImageOptions,
pt *disk.PartitionTable) (*osbuild.Pipeline, error) {
pt *disk.PartitionTable) (pipeline.OSPipeline, error) {
imageConfig := t.getDefaultImageConfig()
p := new(osbuild.Pipeline)
if t.rpmOstree {
p.Name = "ostree-tree"
pl := pipeline.NewOSPipeline(buildPipeline, t.rpmOstree)
pl.PartitionTable = pt
if t.Arch().Name() == distro.S390xArchName {
pl.BootLoader = pipeline.BOOTLOADER_ZIPL
} else {
p.Name = "os"
}
p.Build = "name:build"
if t.rpmOstree && options.OSTree.Parent != "" && options.OSTree.URL != "" {
p.AddStage(osbuild.NewOSTreePasswdStage("org.osbuild.source", options.OSTree.Parent))
pl.BootLoader = pipeline.BOOTLOADER_GRUB
}
rpmOptions := osbuild.NewRPMStageOptions(repos)
rpmOptions.GPGKeysFromTree = imageConfig.GPGKeyFiles
p.AddStage(osbuild.NewRPMStage(rpmOptions, osbuild.NewRpmStageSourceFilesInputs(packages)))
pl.UEFI = t.supportsUEFI()
pl.GRUBLegacy = t.arch.legacy
pl.Vendor = t.arch.distro.vendor
// If the /boot is on a separate partition, the prefix for the BLS stage must be ""
if pt == nil || pt.FindMountable("/boot") == nil {
p.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{}))
} else {
p.AddStage(osbuild.NewFixBLSStage(&osbuild.FixBLSStageOptions{Prefix: common.StringToPtr("")}))
var kernelOptions []string
if t.kernelOptions != "" {
kernelOptions = append(kernelOptions, t.kernelOptions)
}
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
kernelOptions = append(kernelOptions, bpKernel.Append)
}
pl.KernelOptionsAppend = kernelOptions
pl.KernelName = c.GetKernel().Name
language, keyboard := c.GetPrimaryLocale()
if language != nil {
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language}))
} else {
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: imageConfig.Locale}))
}
if keyboard != nil {
p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *keyboard}))
} else if imageConfig.Keyboard != nil {
p.AddStage(osbuild.NewKeymapStage(imageConfig.Keyboard))
}
pl.OSTreeParent = options.OSTree.Parent
pl.OSTreeURL = options.OSTree.URL
if hostname := c.GetHostname(); hostname != nil {
p.AddStage(osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{Hostname: *hostname}))
} else {
p.AddStage(osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{Hostname: "localhost.localdomain"}))
}
timezone, ntpServers := c.GetTimezoneSettings()
if timezone != nil {
p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: *timezone}))
} else {
p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: imageConfig.Timezone}))
}
if len(ntpServers) > 0 {
p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers}))
} else if imageConfig.TimeSynchronization != nil {
p.AddStage(osbuild.NewChronyStage(imageConfig.TimeSynchronization))
}
pl.Repos = repos
pl.PackageSpecs = packages
pl.GPGKeyFiles = imageConfig.GPGKeyFiles
if !t.bootISO {
// don't put users and groups in the payload of an installer
// add them via kickstart instead
if groups := c.GetGroups(); len(groups) > 0 {
p.AddStage(osbuild.NewGroupsStage(osbuild.NewGroupsStageOptions(groups)))
}
if userOptions, err := osbuild.NewUsersStageOptions(c.GetUsers(), false); err != nil {
return nil, err
} else if userOptions != nil {
if t.rpmOstree {
// for ostree, writing the key during user creation is
// redundant and can cause issues so create users without keys
// and write them on first boot
userOptionsSansKeys, err := osbuild.NewUsersStageOptions(c.GetUsers(), true)
if err != nil {
return nil, err
}
p.AddStage(osbuild.NewUsersStage(userOptionsSansKeys))
p.AddStage(osbuild.NewFirstBootStage(usersFirstBootOptions(userOptions)))
} else {
p.AddStage(osbuild.NewUsersStage(userOptions))
}
}
pl.Groups = c.GetGroups()
pl.Users = c.GetUsers()
}
if services := c.GetServices(); services != nil || imageConfig.EnabledServices != nil ||
imageConfig.DisabledServices != nil || imageConfig.DefaultTarget != "" {
p.AddStage(osbuild.NewSystemdStage(systemdStageOptions(
imageConfig.EnabledServices,
imageConfig.DisabledServices,
services,
imageConfig.DefaultTarget,
)))
services := &blueprint.ServicesCustomization{
Enabled: imageConfig.EnabledServices,
Disabled: imageConfig.DisabledServices,
}
if extraServices := c.GetServices(); extraServices != nil {
services.Enabled = append(services.Enabled, extraServices.Enabled...)
services.Disabled = append(services.Disabled, extraServices.Disabled...)
}
pl.EnabledServices = services.Enabled
pl.DisabledServices = services.Disabled
pl.DefaultTarget = imageConfig.DefaultTarget
pl.Firewall = c.GetFirewall()
language, keyboard := c.GetPrimaryLocale()
if language != nil {
pl.Language = *language
} else {
pl.Language = imageConfig.Locale
}
if keyboard != nil {
pl.Keyboard = keyboard
} else if imageConfig.Keyboard != nil {
pl.Keyboard = &imageConfig.Keyboard.Keymap
}
if firewall := c.GetFirewall(); firewall != nil {
p.AddStage(osbuild.NewFirewallStage(firewallStageOptions(firewall)))
if hostname := c.GetHostname(); hostname != nil {
pl.Hostname = *hostname
}
for _, sysconfigConfig := range imageConfig.Sysconfig {
p.AddStage(osbuild.NewSysconfigStage(sysconfigConfig))
timezone, ntpServers := c.GetTimezoneSettings()
if timezone != nil {
pl.Timezone = *timezone
} else {
pl.Timezone = imageConfig.Timezone
}
for _, systemdLogindConfig := range imageConfig.SystemdLogind {
p.AddStage(osbuild.NewSystemdLogindStage(systemdLogindConfig))
if len(ntpServers) > 0 {
pl.NTPServers = ntpServers
} else if imageConfig.TimeSynchronization != nil {
pl.NTPServers = imageConfig.TimeSynchronization.Timeservers
}
for _, cloudInitConfig := range imageConfig.CloudInit {
p.AddStage(osbuild.NewCloudInitStage(cloudInitConfig))
}
pl.Grub2Config = imageConfig.Grub2Config
pl.Sysconfig = imageConfig.Sysconfig
pl.SystemdLogind = imageConfig.SystemdLogind
pl.CloudInit = imageConfig.CloudInit
pl.Modprobe = imageConfig.Modprobe
pl.DracutConf = imageConfig.DracutConf
pl.SystemdUnit = imageConfig.SystemdUnit
pl.Authselect = imageConfig.Authselect
pl.SELinuxConfig = imageConfig.SELinuxConfig
pl.Tuned = imageConfig.Tuned
pl.Tmpfilesd = imageConfig.Tmpfilesd
pl.PamLimitsConf = imageConfig.PamLimitsConf
pl.Sysctld = imageConfig.Sysctld
pl.DNFConfig = imageConfig.DNFConfig
pl.SshdConfig = imageConfig.SshdConfig
pl.AuthConfig = imageConfig.Authconfig
pl.PwQuality = imageConfig.PwQuality
pl.WAAgentConfig = imageConfig.WAAgentConfig
for _, modprobeConfig := range imageConfig.Modprobe {
p.AddStage(osbuild.NewModprobeStage(modprobeConfig))
}
for _, dracutConfConfig := range imageConfig.DracutConf {
p.AddStage(osbuild.NewDracutConfStage(dracutConfConfig))
}
for _, systemdUnitConfig := range imageConfig.SystemdUnit {
p.AddStage(osbuild.NewSystemdUnitStage(systemdUnitConfig))
}
if authselectConfig := imageConfig.Authselect; authselectConfig != nil {
p.AddStage(osbuild.NewAuthselectStage(authselectConfig))
}
if seLinuxConfig := imageConfig.SELinuxConfig; seLinuxConfig != nil {
p.AddStage(osbuild.NewSELinuxConfigStage(seLinuxConfig))
}
if tunedConfig := imageConfig.Tuned; tunedConfig != nil {
p.AddStage(osbuild.NewTunedStage(tunedConfig))
}
for _, tmpfilesdConfig := range imageConfig.Tmpfilesd {
p.AddStage(osbuild.NewTmpfilesdStage(tmpfilesdConfig))
}
for _, pamLimitsConfConfig := range imageConfig.PamLimitsConf {
p.AddStage(osbuild.NewPamLimitsConfStage(pamLimitsConfConfig))
}
for _, sysctldConfig := range imageConfig.Sysctld {
p.AddStage(osbuild.NewSysctldStage(sysctldConfig))
}
for _, dnfConfig := range imageConfig.DNFConfig {
p.AddStage(osbuild.NewDNFConfigStage(dnfConfig))
}
if sshdConfig := imageConfig.SshdConfig; sshdConfig != nil {
p.AddStage((osbuild.NewSshdConfigStage(sshdConfig)))
}
if authConfig := imageConfig.Authconfig; authConfig != nil {
p.AddStage(osbuild.NewAuthconfigStage(authConfig))
}
if pwQuality := imageConfig.PwQuality; pwQuality != nil {
p.AddStage(osbuild.NewPwqualityConfStage(pwQuality))
}
if waConfig := imageConfig.WAAgentConfig; waConfig != nil {
p.AddStage(osbuild.NewWAAgentConfStage(waConfig))
}
if pt != nil {
kernelOptions := osbuild.GenImageKernelOptions(pt)
if t.kernelOptions != "" {
kernelOptions = append(kernelOptions, t.kernelOptions)
}
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
kernelOptions = append(kernelOptions, bpKernel.Append)
}
p = prependKernelCmdlineStage(p, strings.Join(kernelOptions, " "), pt)
p.AddStage(osbuild.NewFSTabStage(osbuild.NewFSTabStageOptions(pt)))
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packages, c.GetKernel().Name)
bootloader := bootloaderConfigStage(t, *pt, kernelVer, false, false)
if cfg := imageConfig.Grub2Config; cfg != nil {
if grub2, ok := bootloader.Options.(*osbuild.GRUB2StageOptions); ok {
// grub2.Config.Default is owned and set by `NewGrub2StageOptionsUnified`
// and thus we need to preserve it
if grub2.Config != nil {
cfg.Default = grub2.Config.Default
}
}
}
p.AddStage(bootloader)
}
p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false)))
if t.rpmOstree {
p.AddStage(osbuild.NewOSTreePrepTreeStage(&osbuild.OSTreePrepTreeStageOptions{
EtcGroupMembers: []string{
// NOTE: We may want to make this configurable.
"wheel", "docker",
},
}))
}
return p, nil
return pl, nil
}
func ostreeCommitPipeline(options distro.ImageOptions, osVersion string) *osbuild.Pipeline {
@ -675,20 +577,6 @@ func qemuPipeline(inputPipelineName, inputFilename, outputFilename string, forma
return p
}
func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, kernelVer string, install, greenboot bool) *osbuild.Stage {
if t.Arch().Name() == distro.S390xArchName {
return osbuild.NewZiplStage(new(osbuild.ZiplStageOptions))
}
uefi := t.supportsUEFI()
legacy := t.arch.legacy
options := osbuild.NewGrub2StageOptionsUnified(&partitionTable, kernelVer, uefi, legacy, t.arch.distro.vendor, install)
options.Greenboot = greenboot
return osbuild.NewGRUB2Stage(options)
}
func bootloaderInstStage(filename string, pt *disk.PartitionTable, arch *architecture, kernelVer string, devices *osbuild.Devices, mounts *osbuild.Mounts, disk *osbuild.Device) *osbuild.Stage {
platform := arch.legacy
if platform != "" {

View file

@ -0,0 +1,312 @@
package pipeline
import (
"fmt"
"path/filepath"
"strings"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/disk"
"github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
type OSPipeline struct {
Pipeline
osTree bool
OSTreeParent string
OSTreeURL string
KernelName string
KernelOptionsAppend []string
BootLoader BootLoader
UEFI bool
GRUBLegacy string
Vendor string
Repos []rpmmd.RepoConfig
PackageSpecs []rpmmd.PackageSpec
GPGKeyFiles []string
PartitionTable *disk.PartitionTable
Language string
Keyboard *string
Hostname string
Timezone string
NTPServers []string
EnabledServices []string
DisabledServices []string
DefaultTarget string
// TODO: drop blueprint types from the API
Groups []blueprint.GroupCustomization
Users []blueprint.UserCustomization
Firewall *blueprint.FirewallCustomization
// TODO: drop osbuild2 types from the API
Grub2Config *osbuild2.GRUB2Config
Sysconfig []*osbuild2.SysconfigStageOptions
SystemdLogind []*osbuild2.SystemdLogindStageOptions
CloudInit []*osbuild2.CloudInitStageOptions
Modprobe []*osbuild2.ModprobeStageOptions
DracutConf []*osbuild2.DracutConfStageOptions
SystemdUnit []*osbuild2.SystemdUnitStageOptions
Authselect *osbuild2.AuthselectStageOptions
SELinuxConfig *osbuild2.SELinuxConfigStageOptions
Tuned *osbuild2.TunedStageOptions
Tmpfilesd []*osbuild2.TmpfilesdStageOptions
PamLimitsConf []*osbuild2.PamLimitsConfStageOptions
Sysctld []*osbuild2.SysctldStageOptions
DNFConfig []*osbuild2.DNFConfigStageOptions
SshdConfig *osbuild2.SshdConfigStageOptions
AuthConfig *osbuild2.AuthconfigStageOptions
PwQuality *osbuild2.PwqualityConfStageOptions
WAAgentConfig *osbuild2.WAAgentConfStageOptions
}
func NewOSPipeline(buildPipeline *BuildPipeline, osTree bool) OSPipeline {
name := "os"
if osTree {
name = "ostree-tree"
}
return OSPipeline{
Pipeline: New(name, &buildPipeline.Pipeline),
osTree: osTree,
Hostname: "localhost.localdomain",
}
}
func (p OSPipeline) Serialize() osbuild2.Pipeline {
pipeline := p.Pipeline.Serialize()
if p.osTree && p.OSTreeParent != "" && p.OSTreeURL != "" {
pipeline.AddStage(osbuild2.NewOSTreePasswdStage("org.osbuild.source", p.OSTreeParent))
}
rpmOptions := osbuild2.NewRPMStageOptions(p.Repos)
rpmOptions.GPGKeysFromTree = p.GPGKeyFiles
pipeline.AddStage(osbuild2.NewRPMStage(rpmOptions, osbuild2.NewRpmStageSourceFilesInputs(p.PackageSpecs)))
// If the /boot is on a separate partition, the prefix for the BLS stage must be ""
if p.PartitionTable == nil || p.PartitionTable.FindMountable("/boot") == nil {
pipeline.AddStage(osbuild2.NewFixBLSStage(&osbuild2.FixBLSStageOptions{}))
} else {
pipeline.AddStage(osbuild2.NewFixBLSStage(&osbuild2.FixBLSStageOptions{Prefix: common.StringToPtr("")}))
}
pipeline.AddStage(osbuild2.NewLocaleStage(&osbuild2.LocaleStageOptions{Language: p.Language}))
if p.Keyboard != nil {
pipeline.AddStage(osbuild2.NewKeymapStage(&osbuild2.KeymapStageOptions{Keymap: *p.Keyboard}))
}
pipeline.AddStage(osbuild2.NewHostnameStage(&osbuild2.HostnameStageOptions{Hostname: p.Hostname}))
pipeline.AddStage(osbuild2.NewTimezoneStage(&osbuild2.TimezoneStageOptions{Zone: p.Timezone}))
if len(p.NTPServers) > 0 {
pipeline.AddStage(osbuild2.NewChronyStage(&osbuild2.ChronyStageOptions{Timeservers: p.NTPServers}))
}
if len(p.Groups) > 0 {
pipeline.AddStage(osbuild2.NewGroupsStage(osbuild2.NewGroupsStageOptions(p.Groups)))
}
if len(p.Users) > 0 {
userOptions, err := osbuild2.NewUsersStageOptions(p.Users, false)
if err != nil {
// TODO: move encryption into weldr
panic("password encryption failed")
}
if p.osTree {
// for ostree, writing the key during user creation is
// redundant and can cause issues so create users without keys
// and write them on first boot
userOptionsSansKeys, err := osbuild2.NewUsersStageOptions(p.Users, true)
if err != nil {
// TODO: move encryption into weldr
panic("password encryption failed")
}
pipeline.AddStage(osbuild2.NewUsersStage(userOptionsSansKeys))
pipeline.AddStage(osbuild2.NewFirstBootStage(usersFirstBootOptions(userOptions)))
} else {
pipeline.AddStage(osbuild2.NewUsersStage(userOptions))
}
}
if p.EnabledServices != nil ||
p.DisabledServices != nil || p.DefaultTarget != "" {
pipeline.AddStage(osbuild2.NewSystemdStage(&osbuild2.SystemdStageOptions{
EnabledServices: p.EnabledServices,
DisabledServices: p.DisabledServices,
DefaultTarget: p.DefaultTarget,
}))
}
if p.Firewall != nil {
options := osbuild2.FirewallStageOptions{
Ports: p.Firewall.Ports,
}
if p.Firewall.Services != nil {
options.EnabledServices = p.Firewall.Services.Enabled
options.DisabledServices = p.Firewall.Services.Disabled
}
pipeline.AddStage(osbuild2.NewFirewallStage(&options))
}
for _, sysconfigConfig := range p.Sysconfig {
pipeline.AddStage(osbuild2.NewSysconfigStage(sysconfigConfig))
}
for _, systemdLogindConfig := range p.SystemdLogind {
pipeline.AddStage(osbuild2.NewSystemdLogindStage(systemdLogindConfig))
}
for _, cloudInitConfig := range p.CloudInit {
pipeline.AddStage(osbuild2.NewCloudInitStage(cloudInitConfig))
}
for _, modprobeConfig := range p.Modprobe {
pipeline.AddStage(osbuild2.NewModprobeStage(modprobeConfig))
}
for _, dracutConfConfig := range p.DracutConf {
pipeline.AddStage(osbuild2.NewDracutConfStage(dracutConfConfig))
}
for _, systemdUnitConfig := range p.SystemdUnit {
pipeline.AddStage(osbuild2.NewSystemdUnitStage(systemdUnitConfig))
}
if p.Authselect != nil {
pipeline.AddStage(osbuild2.NewAuthselectStage(p.Authselect))
}
if p.SELinuxConfig != nil {
pipeline.AddStage(osbuild2.NewSELinuxConfigStage(p.SELinuxConfig))
}
if p.Tuned != nil {
pipeline.AddStage(osbuild2.NewTunedStage(p.Tuned))
}
for _, tmpfilesdConfig := range p.Tmpfilesd {
pipeline.AddStage(osbuild2.NewTmpfilesdStage(tmpfilesdConfig))
}
for _, pamLimitsConfConfig := range p.PamLimitsConf {
pipeline.AddStage(osbuild2.NewPamLimitsConfStage(pamLimitsConfConfig))
}
for _, sysctldConfig := range p.Sysctld {
pipeline.AddStage(osbuild2.NewSysctldStage(sysctldConfig))
}
for _, dnfConfig := range p.DNFConfig {
pipeline.AddStage(osbuild2.NewDNFConfigStage(dnfConfig))
}
if p.SshdConfig != nil {
pipeline.AddStage((osbuild2.NewSshdConfigStage(p.SshdConfig)))
}
if p.AuthConfig != nil {
pipeline.AddStage(osbuild2.NewAuthconfigStage(p.AuthConfig))
}
if p.PwQuality != nil {
pipeline.AddStage(osbuild2.NewPwqualityConfStage(p.PwQuality))
}
if p.WAAgentConfig != nil {
pipeline.AddStage(osbuild2.NewWAAgentConfStage(p.WAAgentConfig))
}
if p.PartitionTable != nil {
kernelOptions := osbuild2.GenImageKernelOptions(p.PartitionTable)
kernelOptions = append(kernelOptions, p.KernelOptionsAppend...)
pipeline = prependKernelCmdlineStage(pipeline, strings.Join(kernelOptions, " "), p.PartitionTable)
pipeline.AddStage(osbuild2.NewFSTabStage(osbuild2.NewFSTabStageOptions(p.PartitionTable)))
var bootloader *osbuild2.Stage
switch p.BootLoader {
case BOOTLOADER_GRUB:
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(p.PackageSpecs, p.KernelName)
options := osbuild2.NewGrub2StageOptionsUnified(p.PartitionTable, kernelVer, p.UEFI, p.GRUBLegacy, p.Vendor, false)
if cfg := p.Grub2Config; cfg != nil {
// TODO: don't store Grub2Config in OSPipeline, making the overrides unnecessary
// grub2.Config.Default is owned and set by `NewGrub2StageOptionsUnified`
// and thus we need to preserve it
if options.Config != nil {
cfg.Default = options.Config.Default
}
options.Config = cfg
}
bootloader = osbuild2.NewGRUB2Stage(options)
case BOOTLOADER_ZIPL:
bootloader = osbuild2.NewZiplStage(new(osbuild2.ZiplStageOptions))
default:
panic("unknown bootloader")
}
pipeline.AddStage(bootloader)
}
pipeline.AddStage(osbuild2.NewSELinuxStage(selinuxStageOptions(false)))
if p.osTree {
pipeline.AddStage(osbuild2.NewOSTreePrepTreeStage(&osbuild2.OSTreePrepTreeStageOptions{
EtcGroupMembers: []string{
// NOTE: We may want to make this configurable.
"wheel", "docker",
},
}))
}
return pipeline
}
func prependKernelCmdlineStage(pipeline osbuild2.Pipeline, kernelOptions string, pt *disk.PartitionTable) osbuild2.Pipeline {
rootFs := pt.FindMountable("/")
if rootFs == nil {
panic("root filesystem must be defined for kernel-cmdline stage, this is a programming error")
}
rootFsUUID := rootFs.GetFSSpec().UUID
kernelStage := osbuild2.NewKernelCmdlineStage(osbuild2.NewKernelCmdlineStageOptions(rootFsUUID, kernelOptions))
pipeline.Stages = append([]*osbuild2.Stage{kernelStage}, pipeline.Stages...)
return pipeline
}
func usersFirstBootOptions(usersStageOptions *osbuild2.UsersStageOptions) *osbuild2.FirstBootStageOptions {
cmds := make([]string, 0, 3*len(usersStageOptions.Users)+2)
// workaround for creating authorized_keys file for user
// need to special case the root user, which has its home in a different place
varhome := filepath.Join("/var", "home")
roothome := filepath.Join("/var", "roothome")
for name, user := range usersStageOptions.Users {
if user.Key != nil {
var home string
if name == "root" {
home = roothome
} else {
home = filepath.Join(varhome, name)
}
sshdir := filepath.Join(home, ".ssh")
cmds = append(cmds, fmt.Sprintf("mkdir -p %s", sshdir))
cmds = append(cmds, fmt.Sprintf("sh -c 'echo %q >> %q'", *user.Key, filepath.Join(sshdir, "authorized_keys")))
cmds = append(cmds, fmt.Sprintf("chown %s:%s -Rc %s", name, name, sshdir))
}
}
cmds = append(cmds, fmt.Sprintf("restorecon -rvF %s", varhome))
cmds = append(cmds, fmt.Sprintf("restorecon -rvF %s", roothome))
options := &osbuild2.FirstBootStageOptions{
Commands: cmds,
WaitForNetwork: false,
}
return options
}

View file

@ -4,6 +4,13 @@ import (
"github.com/osbuild/osbuild-composer/internal/osbuild2"
)
type BootLoader uint64
const (
BOOTLOADER_GRUB BootLoader = iota
BOOTLOADER_ZIPL
)
type Pipeline struct {
name string
runner *string