385 lines
15 KiB
Go
385 lines
15 KiB
Go
package rhel85
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
|
"github.com/osbuild/osbuild-composer/internal/distro"
|
|
osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2"
|
|
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
|
)
|
|
|
|
func edgeInstallerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) {
|
|
pipelines := make([]osbuild.Pipeline, 0)
|
|
pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs["build"]))
|
|
kernelPkg := new(rpmmd.PackageSpec)
|
|
installerPackages := packageSetSpecs["installer"]
|
|
for _, pkg := range installerPackages {
|
|
if pkg.Name == "kernel" {
|
|
kernelPkg = &pkg
|
|
break
|
|
}
|
|
}
|
|
if kernelPkg == nil {
|
|
return nil, fmt.Errorf("kernel package not found in installer package set")
|
|
}
|
|
kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch)
|
|
pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, kernelVer, t.Arch().Name(), anacondaOSTreePayloadStages(options)))
|
|
pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, t.Arch().Name()))
|
|
pipelines = append(pipelines, *bootISOPipeline(t.Filename(), t.Arch().Name()))
|
|
return pipelines, nil
|
|
}
|
|
|
|
func tarInstallerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) {
|
|
pipelines := make([]osbuild.Pipeline, 0)
|
|
pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs["build"]))
|
|
|
|
treePipeline, err := osPipeline(repos, packageSetSpecs["packages"], customizations, options, t.enabledServices, t.disabledServices, t.defaultTarget)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pipelines = append(pipelines, *treePipeline)
|
|
|
|
kernelPkg := new(rpmmd.PackageSpec)
|
|
installerPackages := packageSetSpecs["installer"]
|
|
for _, pkg := range installerPackages {
|
|
if pkg.Name == "kernel" {
|
|
kernelPkg = &pkg
|
|
break
|
|
}
|
|
}
|
|
if kernelPkg == nil {
|
|
return nil, fmt.Errorf("kernel package not found in installer package set")
|
|
}
|
|
kernelVer := fmt.Sprintf("%s-%s.%s", kernelPkg.Version, kernelPkg.Release, kernelPkg.Arch)
|
|
pipelines = append(pipelines, *anacondaTreePipeline(repos, installerPackages, kernelVer, t.Arch().Name(), anacondaTarPayloadStages(options)))
|
|
pipelines = append(pipelines, *bootISOTreePipeline(kernelVer, t.Arch().Name()))
|
|
pipelines = append(pipelines, *bootISOPipeline(t.Filename(), t.Arch().Name()))
|
|
return pipelines, nil
|
|
}
|
|
|
|
func edgeCorePipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec) ([]osbuild.Pipeline, error) {
|
|
pipelines := make([]osbuild.Pipeline, 0)
|
|
pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs["build"]))
|
|
|
|
treePipeline, err := ostreeTreePipeline(repos, packageSetSpecs["packages"], customizations, options, t.enabledServices, t.disabledServices, t.defaultTarget)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pipelines = append(pipelines, *treePipeline)
|
|
pipelines = append(pipelines, *ostreeCommitPipeline(options))
|
|
|
|
return pipelines, nil
|
|
}
|
|
|
|
func edgeCommitPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) {
|
|
pipelines, err := edgeCorePipelines(t, customizations, options, repos, packageSetSpecs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pipelines = append(pipelines, *commitTarPipeline(t.Filename()))
|
|
return pipelines, nil
|
|
}
|
|
|
|
func edgeContainerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, rng *rand.Rand) ([]osbuild.Pipeline, error) {
|
|
pipelines, err := edgeCorePipelines(t, customizations, options, repos, packageSetSpecs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pipelines = append(pipelines, *containerTreePipeline(repos, packageSetSpecs["container"], options, customizations))
|
|
pipelines = append(pipelines, *containerPipeline(t))
|
|
return pipelines, nil
|
|
}
|
|
|
|
func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "build"
|
|
p.Runner = "org.osbuild.rhel85"
|
|
p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(buildPackageSpecs)))
|
|
p.AddStage(osbuild.NewSELinuxStage(selinuxStageOptions(false)))
|
|
return p
|
|
}
|
|
|
|
func coreStages(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, c *blueprint.Customizations, options distro.ImageOptions, enabledServices, disabledServices []string, defaultTarget string) ([]*osbuild.Stage, error) {
|
|
stages := make([]*osbuild.Stage, 0)
|
|
stages = append(stages, osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages)))
|
|
stages = append(stages, osbuild.NewFixBLSStage())
|
|
language, keyboard := c.GetPrimaryLocale()
|
|
if language != nil {
|
|
stages = append(stages, osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language}))
|
|
} else {
|
|
stages = append(stages, osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"}))
|
|
}
|
|
if keyboard != nil {
|
|
stages = append(stages, osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{Keymap: *keyboard}))
|
|
}
|
|
if hostname := c.GetHostname(); hostname != nil {
|
|
stages = append(stages, osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{Hostname: *hostname}))
|
|
}
|
|
|
|
timezone, ntpServers := c.GetTimezoneSettings()
|
|
if timezone != nil {
|
|
stages = append(stages, osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: *timezone}))
|
|
} else {
|
|
stages = append(stages, osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{Zone: "America/New_York"}))
|
|
}
|
|
|
|
if len(ntpServers) > 0 {
|
|
stages = append(stages, osbuild.NewChronyStage(&osbuild.ChronyStageOptions{Timeservers: ntpServers}))
|
|
}
|
|
|
|
if groups := c.GetGroups(); len(groups) > 0 {
|
|
stages = append(stages, osbuild.NewGroupsStage(groupStageOptions(groups)))
|
|
}
|
|
|
|
if users := c.GetUsers(); len(users) > 0 {
|
|
options, err := userStageOptions(users)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stages = append(stages, osbuild.NewUsersStage(options))
|
|
stages = append(stages, osbuild.NewFirstBootStage(usersFirstBootOptions(options)))
|
|
}
|
|
|
|
if services := c.GetServices(); services != nil || enabledServices != nil || disabledServices != nil || defaultTarget != "" {
|
|
stages = append(stages, osbuild.NewSystemdStage(systemdStageOptions(enabledServices, disabledServices, services, defaultTarget)))
|
|
}
|
|
|
|
if firewall := c.GetFirewall(); firewall != nil {
|
|
stages = append(stages, osbuild.NewFirewallStage(firewallStageOptions(firewall)))
|
|
}
|
|
stages = append(stages, osbuild.NewSELinuxStage(selinuxStageOptions(false)))
|
|
|
|
// These are the current defaults for the sysconfig stage. This can be changed to be image type exclusive if different configs are needed.
|
|
stages = append(stages, osbuild.NewSysconfigStage(&osbuild.SysconfigStageOptions{
|
|
Kernel: osbuild.SysconfigKernelOptions{
|
|
UpdateDefault: true,
|
|
DefaultKernel: "kernel",
|
|
},
|
|
Network: osbuild.SysconfigNetworkOptions{
|
|
Networking: true,
|
|
NoZeroConf: true,
|
|
},
|
|
}))
|
|
|
|
if options.Subscription != nil {
|
|
commands := []string{
|
|
fmt.Sprintf("/usr/sbin/subscription-manager register --org=%d --activationkey=%s --serverurl %s --baseurl %s", options.Subscription.Organization, options.Subscription.ActivationKey, options.Subscription.ServerUrl, options.Subscription.BaseUrl),
|
|
}
|
|
if options.Subscription.Insights {
|
|
commands = append(commands, "/usr/bin/insights-client --register")
|
|
}
|
|
|
|
stages = append(stages, osbuild.NewFirstBootStage(&osbuild.FirstBootStageOptions{
|
|
Commands: commands,
|
|
WaitForNetwork: true,
|
|
},
|
|
))
|
|
}
|
|
|
|
return stages, nil
|
|
}
|
|
|
|
func osPipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, c *blueprint.Customizations, options distro.ImageOptions, enabledServices, disabledServices []string, defaultTarget string) (*osbuild.Pipeline, error) {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "os"
|
|
stages, err := coreStages(repos, packages, c, options, enabledServices, disabledServices, defaultTarget)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Stages = stages
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func ostreeTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, c *blueprint.Customizations, options distro.ImageOptions, enabledServices, disabledServices []string, defaultTarget string) (*osbuild.Pipeline, error) {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "ostree-tree"
|
|
p.Build = "name:build"
|
|
|
|
stages, err := coreStages(repos, packages, c, options, enabledServices, disabledServices, defaultTarget)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Stages = stages
|
|
|
|
p.AddStage(osbuild.NewOSTreePrepTreeStage(&osbuild.OSTreePrepTreeStageOptions{
|
|
EtcGroupMembers: []string{
|
|
// NOTE: We may want to make this configurable.
|
|
"wheel", "docker",
|
|
},
|
|
}))
|
|
return p, nil
|
|
}
|
|
func ostreeCommitPipeline(options distro.ImageOptions) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "ostree-commit"
|
|
p.Build = "name:build"
|
|
p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/repo"}))
|
|
|
|
commitStageInput := new(osbuild.OSTreeCommitStageInput)
|
|
commitStageInput.Type = "org.osbuild.tree"
|
|
commitStageInput.Origin = "org.osbuild.pipeline"
|
|
commitStageInput.References = osbuild.OSTreeCommitStageReferences{"name:ostree-tree"}
|
|
|
|
p.AddStage(osbuild.NewOSTreeCommitStage(
|
|
&osbuild.OSTreeCommitStageOptions{
|
|
Ref: options.OSTree.Ref,
|
|
OSVersion: osVersion,
|
|
Parent: options.OSTree.Parent,
|
|
},
|
|
&osbuild.OSTreeCommitStageInputs{Tree: commitStageInput}),
|
|
)
|
|
return p
|
|
}
|
|
|
|
func commitTarPipeline(filename string) *osbuild.Pipeline {
|
|
options := osbuild.TarStageOptions{Filename: filename}
|
|
commitTree := new(osbuild.TarStageInput)
|
|
commitTree.Type = "org.osbuild.tree"
|
|
commitTree.Origin = "org.osbuild.pipeline"
|
|
commitTree.References = []string{"name:ostree-commit"}
|
|
tarStage := osbuild.NewTarStage(&options, &osbuild.TarStageInputs{Tree: commitTree})
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "commit-archive"
|
|
p.Build = "name:build"
|
|
p.AddStage(tarStage)
|
|
return p
|
|
}
|
|
|
|
func containerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, options distro.ImageOptions, c *blueprint.Customizations) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "container-tree"
|
|
p.Build = "name:build"
|
|
p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages)))
|
|
language, _ := c.GetPrimaryLocale()
|
|
if language != nil {
|
|
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: *language}))
|
|
} else {
|
|
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US"}))
|
|
}
|
|
p.AddStage(osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: "/var/www/html/repo"}))
|
|
|
|
p.AddStage(osbuild.NewOSTreePullStage(
|
|
&osbuild.OSTreePullStageOptions{Repo: "/var/www/html/repo"},
|
|
ostreePullStageInputs("org.osbuild.pipeline", "name:ostree-commit", options.OSTree.Ref),
|
|
))
|
|
return p
|
|
}
|
|
|
|
func containerPipeline(t *imageType) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "container"
|
|
p.Build = "name:build"
|
|
options := &osbuild.OCIArchiveStageOptions{
|
|
Architecture: t.arch.Name(),
|
|
Filename: t.Filename(),
|
|
Config: &osbuild.OCIArchiveConfig{
|
|
Cmd: []string{"httpd", "-D", "FOREGROUND"},
|
|
ExposedPorts: []string{"80"},
|
|
},
|
|
}
|
|
baseInput := new(osbuild.OCIArchiveStageInput)
|
|
baseInput.Type = "org.osbuild.tree"
|
|
baseInput.Origin = "org.osbuild.pipeline"
|
|
baseInput.References = []string{"name:container-tree"}
|
|
inputs := &osbuild.OCIArchiveStageInputs{Base: baseInput}
|
|
p.AddStage(osbuild.NewOCIArchiveStage(options, inputs))
|
|
return p
|
|
}
|
|
|
|
func anacondaOSTreePayloadStages(options distro.ImageOptions) []*osbuild.Stage {
|
|
ostreeRepoPath := "/ostree/repo"
|
|
stages := make([]*osbuild.Stage, 0)
|
|
|
|
// ostree commit payload
|
|
stages = append(stages, osbuild.NewOSTreeInitStage(&osbuild.OSTreeInitStageOptions{Path: ostreeRepoPath}))
|
|
stages = append(stages, osbuild.NewOSTreePullStage(
|
|
&osbuild.OSTreePullStageOptions{Repo: ostreeRepoPath},
|
|
ostreePullStageInputs("org.osbuild.source", options.OSTree.Parent, options.OSTree.Ref),
|
|
))
|
|
|
|
// kickstart stage
|
|
stages = append(stages, osbuild.NewKickstartStage(ostreeKickstartStageOptions(fmt.Sprintf("file://%s", ostreeRepoPath), options.OSTree.Ref)))
|
|
|
|
return stages
|
|
}
|
|
|
|
func anacondaTarPayloadStages(options distro.ImageOptions) []*osbuild.Stage {
|
|
tarPath := "/liveimg.tar"
|
|
stages := make([]*osbuild.Stage, 0)
|
|
tree := new(osbuild.TarStageInput)
|
|
tree.Type = "org.osbuild.tree"
|
|
tree.Origin = "org.osbuild.pipeline"
|
|
tree.References = []string{"name:os"}
|
|
tarStage := osbuild.NewTarStage(&osbuild.TarStageOptions{Filename: tarPath}, &osbuild.TarStageInputs{Tree: tree})
|
|
stages = append(stages, tarStage)
|
|
stages = append(stages, osbuild.NewKickstartStage(tarKickstartStageOptions(fmt.Sprintf("file://%s", tarPath))))
|
|
return stages
|
|
}
|
|
|
|
func anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer string, arch string, payloadStages []*osbuild.Stage) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "anaconda-tree"
|
|
p.Build = "name:build"
|
|
p.AddStage(osbuild.NewRPMStage(rpmStageOptions(repos), rpmStageInputs(packages)))
|
|
for _, stage := range payloadStages {
|
|
p.AddStage(stage)
|
|
}
|
|
p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch)))
|
|
p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"}))
|
|
|
|
rootPassword := ""
|
|
rootUser := osbuild.UsersStageOptionsUser{
|
|
Password: &rootPassword,
|
|
}
|
|
|
|
installUID := 0
|
|
installGID := 0
|
|
installHome := "/root"
|
|
installShell := "/usr/libexec/anaconda/run-anaconda"
|
|
installPassword := ""
|
|
installUser := osbuild.UsersStageOptionsUser{
|
|
UID: &installUID,
|
|
GID: &installGID,
|
|
Home: &installHome,
|
|
Shell: &installShell,
|
|
Password: &installPassword,
|
|
}
|
|
usersStageOptions := &osbuild.UsersStageOptions{
|
|
Users: map[string]osbuild.UsersStageOptionsUser{
|
|
"root": rootUser,
|
|
"install": installUser,
|
|
},
|
|
}
|
|
|
|
p.AddStage(osbuild.NewUsersStage(usersStageOptions))
|
|
p.AddStage(osbuild.NewAnacondaStage(anacondaStageOptions()))
|
|
p.AddStage(osbuild.NewLoraxScriptStage(loraxScriptStageOptions(arch)))
|
|
p.AddStage(osbuild.NewDracutStage(dracutStageOptions(kernelVer)))
|
|
|
|
return p
|
|
}
|
|
|
|
func bootISOTreePipeline(kernelVer string, arch string) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "bootiso-tree"
|
|
p.Build = "name:build"
|
|
|
|
p.AddStage(osbuild.NewBootISOMonoStage(bootISOMonoStageOptions(kernelVer, arch), bootISOMonoStageInputs()))
|
|
p.AddStage(osbuild.NewDiscinfoStage(discinfoStageOptions(arch)))
|
|
|
|
return p
|
|
}
|
|
func bootISOPipeline(filename string, arch string) *osbuild.Pipeline {
|
|
p := new(osbuild.Pipeline)
|
|
p.Name = "bootiso"
|
|
p.Build = "name:build"
|
|
|
|
p.AddStage(osbuild.NewXorrisofsStage(xorrisofsStageOptions(filename, arch), xorrisofsStageInputs()))
|
|
p.AddStage(osbuild.NewImplantisomd5Stage(&osbuild.Implantisomd5StageOptions{Filename: filename}))
|
|
|
|
return p
|
|
}
|