diff --git a/internal/distro/rhel8/edge.go b/internal/distro/rhel8/edge.go index 2bfb4ea7b..c1b9e87cb 100644 --- a/internal/distro/rhel8/edge.go +++ b/internal/distro/rhel8/edge.go @@ -131,7 +131,6 @@ func edgeSimplifiedInstallerImgType(rd distribution) imageType { // for other architectures, this will need to be moved to the // architecture and the merging will happen in the PackageSets() // method like the other sets. - buildPkgsKey: edgeSimplifiedInstallerBuildPackageSet, installerPkgsKey: edgeSimplifiedInstallerPackageSet, }, defaultImageConfig: &distro.ImageConfig{ @@ -141,9 +140,9 @@ func edgeSimplifiedInstallerImgType(rd distribution) imageType { rpmOstree: true, bootable: true, bootISO: true, - pipelines: edgeSimplifiedInstallerPipelines, + image: edgeSimplifiedInstallerImage, buildPipelines: []string{"build"}, - payloadPipelines: []string{"image-tree", "image", "archive", "coi-tree", "efiboot-tree", "bootiso-tree", "bootiso"}, + payloadPipelines: []string{"image-tree", "image", "xz", "coi-tree", "efiboot-tree", "bootiso-tree", "bootiso"}, exports: []string{"bootiso"}, basePartitionTables: edgeBasePartitionTables, } @@ -380,22 +379,6 @@ func edgeInstallerBuildPackageSet(t *imageType) rpmmd.PackageSet { ) } -func edgeSimplifiedInstallerBuildPackageSet(t *imageType) rpmmd.PackageSet { - return edgeInstallerBuildPackageSet(t).Append( - edgeEncryptionBuildPackageSet(t), - ) -} - -func edgeEncryptionBuildPackageSet(t *imageType) rpmmd.PackageSet { - return rpmmd.PackageSet{ - Include: []string{ - "clevis", - "clevis-luks", - "cryptsetup", - }, - } -} - func edgeServices(rd distribution) []string { // Common Services var edgeServices = []string{"NetworkManager.service", "firewalld.service", "sshd.service"} diff --git a/internal/distro/rhel8/images.go b/internal/distro/rhel8/images.go index 018916146..18d021739 100644 --- a/internal/distro/rhel8/images.go +++ b/internal/distro/rhel8/images.go @@ -6,6 +6,8 @@ import ( "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/container" "github.com/osbuild/osbuild-composer/internal/distro" + "github.com/osbuild/osbuild-composer/internal/fdo" + "github.com/osbuild/osbuild-composer/internal/ignition" "github.com/osbuild/osbuild-composer/internal/image" "github.com/osbuild/osbuild-composer/internal/manifest" "github.com/osbuild/osbuild-composer/internal/osbuild" @@ -266,3 +268,80 @@ func edgeRawImage(workload workload.Workload, return img, nil } + +func edgeSimplifiedInstallerImage(workload workload.Workload, + t *imageType, + customizations *blueprint.Customizations, + options distro.ImageOptions, + packageSets map[string]rpmmd.PackageSet, + containers []container.Spec, + rng *rand.Rand) (image.ImageKind, error) { + + commit := ostree.CommitSpec{ + Ref: options.OSTree.ImageRef, + URL: options.OSTree.URL, + ContentURL: options.OSTree.ContentURL, + Checksum: options.OSTree.FetchChecksum, + } + rawImg := image.NewOSTreeRawImage(commit) + + rawImg.Users = users.UsersFromBP(customizations.GetUsers()) + rawImg.Groups = users.GroupsFromBP(customizations.GetGroups()) + + // "rw" kernel option is required when /sysroot is mounted read-only to + // keep stateful parts of the filesystem writeable (/var/ and /etc) + rawImg.KernelOptionsAppend = []string{"modprobe.blacklist=vc4", "rw"} + rawImg.Keyboard = "us" + rawImg.Locale = "C.UTF-8" + rawImg.SysrootReadOnly = true + + rawImg.Platform = t.platform + rawImg.Workload = workload + rawImg.Remote = ostree.Remote{ + Name: "rhel-edge", + URL: options.OSTree.URL, + ContentURL: options.OSTree.ContentURL, + } + rawImg.OSName = "redhat" + + // TODO: move generation into LiveImage + pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) + if err != nil { + return nil, err + } + rawImg.PartitionTable = pt + + rawImg.Filename = t.Filename() + + img := image.NewOSTreeSimplifiedInstaller(rawImg, customizations.InstallationDevice) + img.ExtraBasePackages = packageSets[installerPkgsKey] + // img.Workload = workload + img.Platform = t.platform + img.Filename = t.Filename() + if bpFDO := customizations.GetFDO(); bpFDO != nil { + img.FDO = fdo.FromBP(*bpFDO) + } + // ignition configs from blueprint + if bpIgnition := customizations.GetIgnition(); bpIgnition != nil { + if bpIgnition.FirstBoot != nil { + img.IgnitionFirstBoot = ignition.FirstbootOptionsFromBP(*bpIgnition.FirstBoot) + } + if bpIgnition.Embedded != nil { + var err error + img.IgnitionEmbedded, err = ignition.EmbeddedOptionsFromBP(*bpIgnition.Embedded) + if err != nil { + return nil, err + } + } + } + + d := t.arch.distro + img.ISOLabelTempl = d.isolabelTmpl + img.Product = d.product + img.Variant = "edge" + img.OSName = "redhat" + img.OSVersion = d.osVersion + img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"} + + return img, nil +} diff --git a/internal/distro/rhel8/pipelines.go b/internal/distro/rhel8/pipelines.go index 549de1f36..ca8e7a9cf 100644 --- a/internal/distro/rhel8/pipelines.go +++ b/internal/distro/rhel8/pipelines.go @@ -134,31 +134,6 @@ func edgeContainerPipelines(t *imageType, customizations *blueprint.Customizatio return pipelines, nil } -func edgeImagePipelines(t *imageType, customizations *blueprint.Customizations, filename string, options distro.ImageOptions, rng *rand.Rand) ([]osbuild.Pipeline, string, error) { - pipelines := make([]osbuild.Pipeline, 0) - ostreeRepoPath := "/ostree/repo" - imgName := "image.raw" - - partitionTable, err := t.getPartitionTable(nil, options, rng) - if err != nil { - return nil, "", err - } - - // prepare ostree deployment tree - treePipeline := ostreeDeployPipeline(t, partitionTable, ostreeRepoPath, nil, "", rng, customizations, options) - pipelines = append(pipelines, *treePipeline) - - // make raw image from tree - imagePipeline := liveImagePipeline(treePipeline.Name, imgName, partitionTable, t.arch, "") - pipelines = append(pipelines, *imagePipeline) - - // compress image - xzPipeline := xzArchivePipeline(imagePipeline.Name, imgName, filename) - pipelines = append(pipelines, *xzPipeline) - - return pipelines, xzPipeline.Name, nil -} - func buildPipeline(repos []rpmmd.RepoConfig, buildPackageSpecs []rpmmd.PackageSpec, runner string) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "build" @@ -585,263 +560,6 @@ func ostreePayloadStages(options distro.ImageOptions, ostreeRepoPath string) []* return stages } -func edgeSimplifiedInstallerPipelines(t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, repos []rpmmd.RepoConfig, packageSetSpecs map[string][]rpmmd.PackageSpec, containers []container.Spec, rng *rand.Rand) ([]osbuild.Pipeline, error) { - pipelines := make([]osbuild.Pipeline, 0) - pipelines = append(pipelines, *buildPipeline(repos, packageSetSpecs[buildPkgsKey], t.arch.distro.runner.String())) - installerPackages := packageSetSpecs[installerPkgsKey] - kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(installerPackages, "kernel") - imgName := "image.raw.xz" - installDevice := customizations.GetInstallationDevice() - - // create the raw image - imagePipelines, imgPipelineName, err := edgeImagePipelines(t, customizations, imgName, options, rng) - if err != nil { - return nil, err - } - - pipelines = append(pipelines, imagePipelines...) - - // create boot ISO with raw image - d := t.arch.distro - archName := t.Arch().Name() - installerTreePipeline := simplifiedInstallerTreePipeline(repos, installerPackages, kernelVer, archName, d.product, d.osVersion, "edge", customizations.GetFDO()) - isolabel := fmt.Sprintf(d.isolabelTmpl, archName) - efibootTreePipeline := simplifiedInstallerEFIBootTreePipeline(installDevice, kernelVer, archName, d.vendor, d.product, d.osVersion, isolabel, customizations.GetFDO()) - bootISOTreePipeline := simplifiedInstallerBootISOTreePipeline(imgPipelineName, kernelVer, rng) - - pipelines = append(pipelines, *installerTreePipeline, *efibootTreePipeline, *bootISOTreePipeline) - pipelines = append(pipelines, *bootISOPipeline(t.Filename(), d.isolabelTmpl, t.Arch().Name(), false)) - - return pipelines, nil -} - -func simplifiedInstallerBootISOTreePipeline(archivePipelineName, kver string, rng *rand.Rand) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "bootiso-tree" - p.Build = "name:build" - - p.AddStage(osbuild.NewCopyStageSimple( - &osbuild.CopyStageOptions{ - Paths: []osbuild.CopyStagePath{ - { - From: "input://file/image.raw.xz", - To: "tree:///image.raw.xz", - }, - }, - }, - osbuild.NewFilesInputs(osbuild.NewFilesInputReferencesPipeline(archivePipelineName, "image.raw.xz")), - )) - - p.AddStage(osbuild.NewMkdirStage( - &osbuild.MkdirStageOptions{ - Paths: []osbuild.Path{ - { - Path: "images", - }, - { - Path: "images/pxeboot", - }, - }, - }, - )) - - pt := disk.PartitionTable{ - Size: 20971520, - SectorSize: 512, - Partitions: []disk.Partition{ - { - Start: 0, - Size: 20971520, - Payload: &disk.Filesystem{ - Type: "vfat", - Mountpoint: "/", - UUID: disk.NewVolIDFromRand(rng), - }, - }, - }, - } - - filename := "images/efiboot.img" - loopback := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: filename}) - p.AddStage(osbuild.NewTruncateStage(&osbuild.TruncateStageOptions{Filename: filename, Size: fmt.Sprintf("%d", pt.Size)})) - - for _, stage := range osbuild.GenMkfsStages(&pt, loopback) { - p.AddStage(stage) - } - - inputName := "root-tree" - copyInputs := osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree") - copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, "efiboot-tree", filename, &pt) - p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts)) - - inputName = "coi" - copyInputs = osbuild.NewPipelineTreeInputs(inputName, "coi-tree") - p.AddStage(osbuild.NewCopyStageSimple( - &osbuild.CopyStageOptions{ - Paths: []osbuild.CopyStagePath{ - { - From: fmt.Sprintf("input://%s/boot/vmlinuz-%s", inputName, kver), - To: "tree:///images/pxeboot/vmlinuz", - }, - { - From: fmt.Sprintf("input://%s/boot/initramfs-%s.img", inputName, kver), - To: "tree:///images/pxeboot/initrd.img", - }, - }, - }, - copyInputs, - )) - - inputName = "efi-tree" - copyInputs = osbuild.NewPipelineTreeInputs(inputName, "efiboot-tree") - p.AddStage(osbuild.NewCopyStageSimple( - &osbuild.CopyStageOptions{ - Paths: []osbuild.CopyStagePath{ - { - From: fmt.Sprintf("input://%s/EFI", inputName), - To: "tree:///", - }, - }, - }, - copyInputs, - )) - - return p -} - -func simplifiedInstallerEFIBootTreePipeline(installDevice, kernelVer, arch, vendor, product, osVersion, isolabel string, fdo *blueprint.FDOCustomization) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "efiboot-tree" - p.Build = "name:build" - p.AddStage(osbuild.NewGrubISOStage(grubISOStageOptions(installDevice, kernelVer, arch, vendor, product, osVersion, isolabel, fdo))) - return p -} - -func simplifiedInstallerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string, fdo *blueprint.FDOCustomization) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "coi-tree" - p.Build = "name:build" - p.AddStage(osbuild.NewRPMStage(osbuild.NewRPMStageOptions(repos), osbuild.NewRpmStageSourceFilesInputs(packages))) - p.AddStage(osbuild.NewBuildstampStage(buildStampStageOptions(arch, product, osVersion, variant))) - p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: "en_US.UTF-8"})) - dracutStageOptions := dracutStageOptions(kernelVer, arch, []string{ - "coreos-installer", - "fdo", - }) - if fdo.HasFDO() && fdo.DiunPubKeyRootCerts != "" { - p.AddStage(osbuild.NewFDOStageForRootCerts(fdo.DiunPubKeyRootCerts)) - dracutStageOptions.Install = []string{"/fdo_diun_pub_key_root_certs.pem"} - } - p.AddStage(osbuild.NewDracutStage(dracutStageOptions)) - - return p -} - -func ostreeDeployPipeline( - t *imageType, - pt *disk.PartitionTable, - repoPath string, - kernel *blueprint.KernelCustomization, - kernelVer string, - rng *rand.Rand, - c *blueprint.Customizations, - options distro.ImageOptions, -) *osbuild.Pipeline { - - p := new(osbuild.Pipeline) - p.Name = "image-tree" - p.Build = "name:build" - osname := "redhat" - remote := "rhel-edge" - - p.AddStage(osbuild.OSTreeInitFsStage()) - p.AddStage(osbuild.NewOSTreePullStage( - &osbuild.OSTreePullStageOptions{Repo: repoPath, Remote: remote}, - osbuild.NewOstreePullStageInputs("org.osbuild.source", options.OSTree.FetchChecksum, options.OSTree.ImageRef), - )) - p.AddStage(osbuild.NewOSTreeOsInitStage( - &osbuild.OSTreeOsInitStageOptions{ - OSName: osname, - }, - )) - p.AddStage(osbuild.NewOSTreeConfigStage(ostreeConfigStageOptions(repoPath, true))) - p.AddStage(osbuild.NewMkdirStage(efiMkdirStageOptions())) - kernelOpts := osbuild.GenImageKernelOptions(pt) - // "rw" kernel option is required when /sysroot is mounted read-only to - // keep stateful parts of the filesystem writeable (/var/ and /etc) - kernelOpts = append(kernelOpts, "rw") - p.AddStage(osbuild.NewOSTreeDeployStage( - &osbuild.OSTreeDeployStageOptions{ - OsName: osname, - Ref: options.OSTree.ImageRef, - Remote: remote, - Mounts: []string{"/boot", "/boot/efi"}, - Rootfs: osbuild.Rootfs{ - Label: "root", - }, - KernelOpts: kernelOpts, - }, - )) - - if options.OSTree.URL != "" { - p.AddStage(osbuild.NewOSTreeRemotesStage( - &osbuild.OSTreeRemotesStageOptions{ - Repo: "/ostree/repo", - Remotes: []osbuild.OSTreeRemote{ - { - Name: remote, - URL: options.OSTree.URL, - }, - }, - }, - )) - } - - p.AddStage(osbuild.NewOSTreeFillvarStage( - &osbuild.OSTreeFillvarStageOptions{ - Deployment: osbuild.OSTreeDeployment{ - OSName: osname, - Ref: options.OSTree.ImageRef, - }, - }, - )) - - fstabOptions := osbuild.NewFSTabStageOptions(pt) - fstabOptions.OSTree = &osbuild.OSTreeFstab{ - Deployment: osbuild.OSTreeDeployment{ - OSName: osname, - Ref: options.OSTree.ImageRef, - }, - } - p.AddStage(osbuild.NewFSTabStage(fstabOptions)) - - if bpUsers := c.GetUsers(); len(bpUsers) > 0 { - usersStage, err := osbuild.GenUsersStage(users.UsersFromBP(bpUsers), false) - if err != nil { - panic(err) - } - usersStage.MountOSTree(osname, options.OSTree.ImageRef, 0) - p.AddStage(usersStage) - } - if bpGroups := c.GetGroups(); len(bpGroups) > 0 { - groupsStage := osbuild.GenGroupsStage(users.GroupsFromBP(bpGroups)) - groupsStage.MountOSTree(osname, options.OSTree.ImageRef, 0) - p.AddStage(groupsStage) - } - - p.AddStage(bootloaderConfigStage(t, *pt, kernel, kernelVer, true, true)) - - p.AddStage(osbuild.NewOSTreeSelinuxStage( - &osbuild.OSTreeSelinuxStageOptions{ - Deployment: osbuild.OSTreeDeployment{ - OSName: osname, - Ref: options.OSTree.ImageRef, - }, - }, - )) - return p -} - func anacondaTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.PackageSpec, kernelVer, arch, product, osVersion, variant string, users bool) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = "anaconda-tree" @@ -924,43 +642,6 @@ func bootISOPipeline(filename, isolabel, arch string, isolinux bool) *osbuild.Pi return p } -func liveImagePipeline(inputPipelineName string, outputFilename string, pt *disk.PartitionTable, arch *architecture, kernelVer string) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "image" - p.Build = "name:build" - - for _, stage := range osbuild.GenImagePrepareStages(pt, outputFilename, osbuild.PTSfdisk) { - p.AddStage(stage) - } - - inputName := "root-tree" - copyOptions, copyDevices, copyMounts := osbuild.GenCopyFSTreeOptions(inputName, inputPipelineName, outputFilename, pt) - copyInputs := osbuild.NewPipelineTreeInputs(inputName, inputPipelineName) - p.AddStage(osbuild.NewCopyStage(copyOptions, copyInputs, copyDevices, copyMounts)) - - for _, stage := range osbuild.GenImageFinishStages(pt, outputFilename) { - p.AddStage(stage) - } - - loopback := osbuild.NewLoopbackDevice(&osbuild.LoopbackDeviceOptions{Filename: outputFilename}) - p.AddStage(bootloaderInstStage(outputFilename, pt, arch, kernelVer, copyDevices, copyMounts, loopback)) - - return p -} - -func xzArchivePipeline(inputPipelineName, inputFilename, outputFilename string) *osbuild.Pipeline { - p := new(osbuild.Pipeline) - p.Name = "archive" - p.Build = "name:build" - - p.AddStage(osbuild.NewXzStage( - osbuild.NewXzStageOptions(outputFilename), - osbuild.NewFilesInputs(osbuild.NewFilesInputReferencesPipeline(inputPipelineName, inputFilename)), - )) - - return p -} - func tarArchivePipeline(name, inputPipelineName string, tarOptions *osbuild.TarStageOptions) *osbuild.Pipeline { p := new(osbuild.Pipeline) p.Name = name @@ -990,16 +671,3 @@ func bootloaderConfigStage(t *imageType, partitionTable disk.PartitionTable, ker 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 != "" { - return osbuild.NewGrub2InstStage(osbuild.NewGrub2InstStageOption(filename, pt, platform)) - } - - if arch.name == distro.S390xArchName { - return osbuild.NewZiplInstStage(osbuild.NewZiplInstStageOptions(kernelVer, pt), disk, devices, mounts) - } - - return nil -} diff --git a/internal/distro/rhel8/stage_options.go b/internal/distro/rhel8/stage_options.go index 5caffd138..58d0dde14 100644 --- a/internal/distro/rhel8/stage_options.go +++ b/internal/distro/rhel8/stage_options.go @@ -2,7 +2,6 @@ package rhel8 import ( "fmt" - "os" "path/filepath" "github.com/osbuild/osbuild-composer/internal/blueprint" @@ -215,51 +214,6 @@ func bootISOMonoStageOptions(kernelVer, arch, vendor, product, osVersion, isolab } } -func grubISOStageOptions(installDevice, kernelVer, arch, vendor, product, osVersion, isolabel string, fdo *blueprint.FDOCustomization) *osbuild.GrubISOStageOptions { - var architectures []string - - if arch == distro.X86_64ArchName { - architectures = []string{"IA32", "X64"} - } else if arch == distro.Aarch64ArchName { - architectures = []string{"AA64"} - } else { - panic("unsupported architecture") - } - - grubISOStageOptions := &osbuild.GrubISOStageOptions{ - Product: osbuild.Product{ - Name: product, - Version: osVersion, - }, - ISOLabel: isolabel, - Kernel: osbuild.ISOKernel{ - Dir: "/images/pxeboot", - Opts: []string{"rd.neednet=1", - "coreos.inst.crypt_root=1", - "coreos.inst.isoroot=" + isolabel, - "coreos.inst.install_dev=" + installDevice, - "coreos.inst.image_file=/run/media/iso/image.raw.xz", - "coreos.inst.insecure"}, - }, - Architectures: architectures, - Vendor: vendor, - } - - if fdo.HasFDO() { - grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.manufacturing_server_url="+fdo.ManufacturingServerURL) - if fdo.DiunPubKeyInsecure != "" { - grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_insecure="+fdo.DiunPubKeyInsecure) - } - if fdo.DiunPubKeyHash != "" { - grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_hash="+fdo.DiunPubKeyHash) - } - if fdo.DiunPubKeyRootCerts != "" { - grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_root_certs=/fdo_diun_pub_key_root_certs.pem") - } - } - return grubISOStageOptions -} - func discinfoStageOptions(arch string) *osbuild.DiscinfoStageOptions { return &osbuild.DiscinfoStageOptions{ BaseArch: arch, @@ -309,26 +263,3 @@ func chmodStageOptions(path, mode string, recursive bool) *osbuild.ChmodStageOpt }, } } - -func ostreeConfigStageOptions(repo string, readOnly bool) *osbuild.OSTreeConfigStageOptions { - return &osbuild.OSTreeConfigStageOptions{ - Repo: repo, - Config: &osbuild.OSTreeConfig{ - Sysroot: &osbuild.SysrootOptions{ - ReadOnly: common.ToPtr(readOnly), - Bootloader: "none", - }, - }, - } -} - -func efiMkdirStageOptions() *osbuild.MkdirStageOptions { - return &osbuild.MkdirStageOptions{ - Paths: []osbuild.Path{ - { - Path: "/boot/efi", - Mode: os.FileMode(0700), - }, - }, - } -}