From 1d743f048a14dd858d04559658288fc00d5cc244 Mon Sep 17 00:00:00 2001 From: Major Hayden Date: Tue, 21 Apr 2020 08:18:02 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=A3=20Add=20initial=20RHEL=208.3=20sup?= =?UTF-8?q?port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The osbuild changes were made in osbuild/osbuild#341. Signed-off-by: Major Hayden --- cmd/osbuild-composer/main.go | 3 +- cmd/osbuild-pipeline/main.go | 3 +- internal/distro/distro_test.go | 6 +- internal/distro/rhel83/distro.go | 866 ++++++++++++++++++++++++++ internal/distro/rhel83/distro_test.go | 95 +++ repositories/rhel-8.3.json | 26 + 6 files changed, 995 insertions(+), 4 deletions(-) create mode 100644 internal/distro/rhel83/distro.go create mode 100644 internal/distro/rhel83/distro_test.go create mode 100644 repositories/rhel-8.3.json diff --git a/cmd/osbuild-composer/main.go b/cmd/osbuild-composer/main.go index 04af74f3e..abb903cbe 100644 --- a/cmd/osbuild-composer/main.go +++ b/cmd/osbuild-composer/main.go @@ -14,6 +14,7 @@ import ( "github.com/osbuild/osbuild-composer/internal/distro/fedora32" "github.com/osbuild/osbuild-composer/internal/distro/rhel81" "github.com/osbuild/osbuild-composer/internal/distro/rhel82" + "github.com/osbuild/osbuild-composer/internal/distro/rhel83" "github.com/osbuild/osbuild-composer/internal/rcm" "github.com/osbuild/osbuild-composer/internal/common" @@ -90,7 +91,7 @@ func main() { rpm := rpmmd.NewRPMMD(path.Join(cacheDirectory, "rpmmd")) - distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New()) + distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New(), rhel83.New()) if err != nil { log.Fatalf("Error loading distros: %v", err) } diff --git a/cmd/osbuild-pipeline/main.go b/cmd/osbuild-pipeline/main.go index 7d93ea676..f05e262a2 100644 --- a/cmd/osbuild-pipeline/main.go +++ b/cmd/osbuild-pipeline/main.go @@ -14,6 +14,7 @@ import ( "github.com/osbuild/osbuild-composer/internal/distro/fedora32" "github.com/osbuild/osbuild-composer/internal/distro/rhel81" "github.com/osbuild/osbuild-composer/internal/distro/rhel82" + "github.com/osbuild/osbuild-composer/internal/distro/rhel83" "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/distro" @@ -71,7 +72,7 @@ func main() { } } - distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New()) + distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New(), rhel83.New()) if err != nil { panic(err) } diff --git a/internal/distro/distro_test.go b/internal/distro/distro_test.go index c7605a841..daf24bb89 100644 --- a/internal/distro/distro_test.go +++ b/internal/distro/distro_test.go @@ -16,6 +16,7 @@ import ( "github.com/osbuild/osbuild-composer/internal/distro/fedora32" "github.com/osbuild/osbuild-composer/internal/distro/rhel81" "github.com/osbuild/osbuild-composer/internal/distro/rhel82" + "github.com/osbuild/osbuild-composer/internal/distro/rhel83" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) @@ -70,7 +71,7 @@ func TestDistro_Manifest(t *testing.T) { } } t.Run(tt.ComposeRequest.ImageType, func(t *testing.T) { - distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New()) + distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New(), rhel83.New()) if err != nil { t.Fatal(err) } @@ -107,9 +108,10 @@ func TestDistro_RegistryList(t *testing.T) { "fedora-32", "rhel-8.1", "rhel-8.2", + "rhel-8.3", } - distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New()) + distros, err := distro.NewRegistry(fedora30.New(), fedora31.New(), fedora32.New(), rhel81.New(), rhel82.New(), rhel83.New()) require.NoError(t, err) require.Equalf(t, expected, distros.List(), "unexpected list of distros") diff --git a/internal/distro/rhel83/distro.go b/internal/distro/rhel83/distro.go new file mode 100644 index 000000000..5c230f388 --- /dev/null +++ b/internal/distro/rhel83/distro.go @@ -0,0 +1,866 @@ +package rhel83 + +import ( + "errors" + "sort" + "strconv" + + "github.com/osbuild/osbuild-composer/internal/distro" + "github.com/osbuild/osbuild-composer/internal/osbuild" + + "github.com/google/uuid" + + "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/crypt" + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + +const name = "rhel-8.3" +const modulePlatformID = "platform:el8" + +type RHEL83 struct { + arches map[string]arch + imageTypes map[string]imageType + buildPackages []string +} + +type arch struct { + name string + bootloaderPackages []string + buildPackages []string + uefi bool +} + +type imageType struct { + name string + mimeType string + packages []string + excludedPackages []string + enabledServices []string + disabledServices []string + bootable bool + defaultTarget string + kernelOptions string + defaultSize uint64 + assembler func(uefi bool, size uint64) *osbuild.Assembler +} + +type rhel83Arch struct { + name string + distro *RHEL83 + arch *arch +} + +type rhel83ImageType struct { + name string + arch *rhel83Arch + imageType *imageType +} + +func (d *RHEL83) ListArches() []string { + archs := make([]string, 0, len(d.arches)) + for name := range d.arches { + archs = append(archs, name) + } + sort.Strings(archs) + return archs +} + +func (d *RHEL83) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &rhel83Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *rhel83Arch) Name() string { + return a.name +} + +func (a *rhel83Arch) ListImageTypes() []string { + formats := make([]string, 0, len(a.distro.imageTypes)) + for name := range a.distro.imageTypes { + formats = append(formats, name) + } + sort.Strings(formats) + return formats +} + +func (a *rhel83Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.imageTypes[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &rhel83ImageType{ + name: imageType, + arch: a, + imageType: &t, + }, nil +} + +func (t *rhel83ImageType) Name() string { + return t.name +} + +func (t *rhel83ImageType) Filename() string { + return t.imageType.name +} + +func (t *rhel83ImageType) MIMEType() string { + return t.imageType.mimeType +} + +func (t *rhel83ImageType) Size(size uint64) uint64 { + const MegaByte = 1024 * 1024 + // Microsoft Azure requires vhd images to be rounded up to the nearest MB + if t.name == "vhd" && size%MegaByte != 0 { + size = (size/MegaByte + 1) * MegaByte + } + if size == 0 { + size = t.imageType.defaultSize + } + return size +} + +func (t *rhel83ImageType) BasePackages() ([]string, []string) { + packages := t.imageType.packages + if t.imageType.bootable { + packages = append(packages, t.arch.arch.bootloaderPackages...) + } + + return packages, t.imageType.excludedPackages +} + +func (t *rhel83ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.buildPackages...) +} + +func (t *rhel83ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.pipeline(c, repos, packageSpecs, buildPackageSpecs, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + +func New() *RHEL83 { + const GigaByte = 1024 * 1024 * 1024 + + r := RHEL83{ + imageTypes: map[string]imageType{}, + buildPackages: []string{ + "dnf", + "dosfstools", + "e2fsprogs", + "glibc", + "policycoreutils", + "python36", + "qemu-img", + "systemd", + "tar", + "xfsprogs", + "xz", + }, + arches: map[string]arch{ + "x86_64": arch{ + name: "x86_64", + bootloaderPackages: []string{ + "dracut-config-generic", + "grub2-pc", + }, + buildPackages: []string{ + "grub2-pc", + }, + }, + "aarch64": arch{ + name: "aarch64", + bootloaderPackages: []string{ + "dracut-config-generic", + "efibootmgr", + "grub2-efi-aa64", + "grub2-tools", + "shim-aa64", + }, + uefi: true, + }, + }, + } + + r.imageTypes["ami"] = imageType{ + name: "image.raw.xz", + mimeType: "application/octet-stream", + packages: []string{ + "checkpolicy", + "chrony", + "cloud-init", + "cloud-init", + "cloud-utils-growpart", + "@core", + "dhcp-client", + "gdisk", + "insights-client", + "kernel", + "langpacks-en", + "net-tools", + "NetworkManager", + "redhat-release", + "redhat-release-eula", + "rng-tools", + "rsync", + "selinux-policy-targeted", + "tar", + "yum-utils", + + // TODO this doesn't exist in BaseOS or AppStream + // "rh-amazon-rhui-client", + }, + excludedPackages: []string{ + "aic94xx-firmware", + "alsa-firmware", + "alsa-lib", + "alsa-tools-firmware", + "biosdevname", + "dracut-config-rescue", + "firewalld", + "iprutils", + "ivtv-firmware", + "iwl1000-firmware", + "iwl100-firmware", + "iwl105-firmware", + "iwl135-firmware", + "iwl2000-firmware", + "iwl2030-firmware", + "iwl3160-firmware", + "iwl3945-firmware", + "iwl4965-firmware", + "iwl5000-firmware", + "iwl5150-firmware", + "iwl6000-firmware", + "iwl6000g2a-firmware", + "iwl6000g2b-firmware", + "iwl6050-firmware", + "iwl7260-firmware", + "libertas-sd8686-firmware", + "libertas-sd8787-firmware", + "libertas-usb8388-firmware", + "plymouth", + + // TODO this cannot be removed, because the kernel (?) + // depends on it. The ec2 kickstart force-removes it. + // "linux-firmware", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + defaultTarget: "multi-user.target", + bootable: true, + kernelOptions: "ro console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 crashkernel=auto", + defaultSize: 6 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("raw.xz", "image.raw.xz", uefi, size) + }, + } + + r.imageTypes["ext4-filesystem"] = imageType{ + name: "filesystem.img", + mimeType: "application/octet-stream", + packages: []string{ + "policycoreutils", + "selinux-policy-targeted", + "kernel", + "firewalld", + "chrony", + "langpacks-en", + }, + excludedPackages: []string{ + "dracut-config-rescue", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + bootable: false, + kernelOptions: "ro net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { return r.rawFSAssembler("filesystem.img", size) }, + } + + r.imageTypes["partitioned-disk"] = imageType{ + name: "disk.img", + mimeType: "application/octet-stream", + packages: []string{ + "@core", + "chrony", + "firewalld", + "kernel", + "langpacks-en", + "selinux-policy-targeted", + }, + excludedPackages: []string{ + "dracut-config-rescue", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + bootable: true, + kernelOptions: "ro net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("raw", "disk.img", uefi, size) + }, + } + + r.imageTypes["qcow2"] = imageType{ + name: "disk.qcow2", + mimeType: "application/x-qemu-disk", + packages: []string{ + "@core", + "chrony", + "dnf", + "kernel", + "yum", + "nfs-utils", + "dnf-utils", + "cloud-init", + "python3-jsonschema", + "qemu-guest-agent", + "cloud-utils-growpart", + "dracut-norescue", + "tar", + "tcpdump", + "rsync", + "dnf-plugin-spacewalk", + "rhn-client-tools", + "rhnlib", + "rhnsd", + "rhn-setup", + "NetworkManager", + "dhcp-client", + "cockpit-ws", + "cockpit-system", + "subscription-manager-cockpit", + "redhat-release", + "redhat-release-eula", + "rng-tools", + "insights-client", + // TODO: rh-amazon-rhui-client + }, + excludedPackages: []string{ + "dracut-config-rescue", + "aic94xx-firmware", + "alsa-firmware", + "alsa-lib", + "alsa-tools-firmware", + "firewalld", + "ivtv-firmware", + "iwl1000-firmware", + "iwl100-firmware", + "iwl105-firmware", + "iwl135-firmware", + "iwl2000-firmware", + "iwl2030-firmware", + "iwl3160-firmware", + "iwl3945-firmware", + "iwl4965-firmware", + "iwl5000-firmware", + "iwl5150-firmware", + "iwl6000-firmware", + "iwl6000g2a-firmware", + "iwl6000g2b-firmware", + "iwl6050-firmware", + "iwl7260-firmware", + "libertas-sd8686-firmware", + "libertas-sd8787-firmware", + "libertas-usb8388-firmware", + "langpacks-*", + "langpacks-en", + "biosdevname", + "plymouth", + "iprutils", + "langpacks-en", + "fedora-release", + "fedora-repos", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + bootable: true, + kernelOptions: "console=ttyS0 console=ttyS0,115200n8 no_timer_check crashkernel=auto net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("qcow2", "disk.qcow2", uefi, size) + }, + } + + r.imageTypes["openstack"] = imageType{ + name: "disk.qcow2", + mimeType: "application/x-qemu-disk", + packages: []string{ + // Defaults + "@Core", + "langpacks-en", + + // From the lorax kickstart + "kernel", + "selinux-policy-targeted", + "cloud-init", + "qemu-guest-agent", + "spice-vdagent", + }, + excludedPackages: []string{ + "dracut-config-rescue", + }, + bootable: true, + kernelOptions: "ro net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("qcow2", "disk.qcow2", uefi, size) + }, + } + + r.imageTypes["tar"] = imageType{ + name: "root.tar.xz", + mimeType: "application/x-tar", + packages: []string{ + "policycoreutils", + "selinux-policy-targeted", + "kernel", + "firewalld", + "chrony", + "langpacks-en", + }, + excludedPackages: []string{ + "dracut-config-rescue", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + bootable: false, + kernelOptions: "ro net.ifnames=0", + assembler: func(uefi bool, size uint64) *osbuild.Assembler { return r.tarAssembler("root.tar.xz", "xz") }, + } + + r.imageTypes["vhd"] = imageType{ + name: "disk.vhd", + mimeType: "application/x-vhd", + packages: []string{ + // Defaults + "@Core", + "langpacks-en", + + // From the lorax kickstart + "kernel", + "selinux-policy-targeted", + "chrony", + "WALinuxAgent", + "python3", + "net-tools", + "cloud-init", + "cloud-utils-growpart", + "gdisk", + }, + excludedPackages: []string{ + "dracut-config-rescue", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + enabledServices: []string{ + "sshd", + "waagent", + }, + defaultTarget: "multi-user.target", + bootable: true, + kernelOptions: "ro biosdevname=0 rootdelay=300 console=ttyS0 earlyprintk=ttyS0 net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("vpc", "disk.vhd", uefi, size) + }, + } + + r.imageTypes["vmdk"] = imageType{ + name: "disk.vmdk", + mimeType: "application/x-vmdk", + packages: []string{ + "@core", + "chrony", + "firewalld", + "kernel", + "langpacks-en", + "open-vm-tools", + "selinux-policy-targeted", + }, + excludedPackages: []string{ + "dracut-config-rescue", + + // TODO setfiles failes because of usr/sbin/timedatex. Exlude until + // https://errata.devel.redhat.com/advisory/47339 lands + "timedatex", + }, + bootable: true, + kernelOptions: "ro net.ifnames=0", + defaultSize: 2 * GigaByte, + assembler: func(uefi bool, size uint64) *osbuild.Assembler { + return r.qemuAssembler("vmdk", "disk.vmdk", uefi, size) + }, + } + + return &r +} + +func (r *RHEL83) Name() string { + return name +} + +func (r *RHEL83) ModulePlatformID() string { + return modulePlatformID +} + +func (r *RHEL83) BasePackages(outputFormat string, outputArchitecture string) ([]string, []string, error) { + output, exists := r.imageTypes[outputFormat] + if !exists { + return nil, nil, errors.New("invalid output format: " + outputFormat) + } + + packages := output.packages + if output.bootable { + arch, exists := r.arches[outputArchitecture] + if !exists { + return nil, nil, errors.New("invalid architecture: " + outputArchitecture) + } + + packages = append(packages, arch.bootloaderPackages...) + } + + return packages, output.excludedPackages, nil +} + +func (r *RHEL83) BuildPackages(outputArchitecture string) ([]string, error) { + arch, exists := r.arches[outputArchitecture] + if !exists { + return nil, errors.New("invalid architecture: " + outputArchitecture) + } + + return append(r.buildPackages, arch.buildPackages...), nil +} + +func sources(packages []rpmmd.PackageSpec) *osbuild.Sources { + files := &osbuild.FilesSource{ + URLs: make(map[string]string), + } + for _, pkg := range packages { + files.URLs[pkg.Checksum] = pkg.RemoteLocation + } + return &osbuild.Sources{ + "org.osbuild.files": files, + } +} + +func (t *rhel83ImageType) pipeline(c *blueprint.Customizations, repos []rpmmd.RepoConfig, packageSpecs, buildPackageSpecs []rpmmd.PackageSpec, size uint64) (*osbuild.Pipeline, error) { + p := &osbuild.Pipeline{} + p.SetBuild(t.buildPipeline(repos, *t.arch.arch, buildPackageSpecs), "org.osbuild.rhel83") + + p.AddStage(osbuild.NewRPMStage(t.rpmStageOptions(*t.arch.arch, repos, packageSpecs))) + p.AddStage(osbuild.NewFixBLSStage()) + + if t.imageType.bootable { + p.AddStage(osbuild.NewFSTabStage(t.fsTabStageOptions(t.arch.arch.uefi))) + } + + kernelOptions := t.imageType.kernelOptions + if kernel := c.GetKernel(); kernel != nil { + kernelOptions += " " + kernel.Append + } + p.AddStage(osbuild.NewGRUB2Stage(t.grub2StageOptions(kernelOptions, t.arch.arch.uefi))) + + // TODO support setting all languages and install corresponding langpack-* package + language, keyboard := c.GetPrimaryLocale() + + if language != nil { + p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{*language})) + } else { + p.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{"en_US"})) + } + + if keyboard != nil { + p.AddStage(osbuild.NewKeymapStage(&osbuild.KeymapStageOptions{*keyboard})) + } + + if hostname := c.GetHostname(); hostname != nil { + p.AddStage(osbuild.NewHostnameStage(&osbuild.HostnameStageOptions{*hostname})) + } + + timezone, ntpServers := c.GetTimezoneSettings() + + // TODO install chrony when this is set? + if timezone != nil { + p.AddStage(osbuild.NewTimezoneStage(&osbuild.TimezoneStageOptions{*timezone})) + } + + if len(ntpServers) > 0 { + p.AddStage(osbuild.NewChronyStage(&osbuild.ChronyStageOptions{ntpServers})) + } + + if users := c.GetUsers(); len(users) > 0 { + options, err := t.userStageOptions(users) + if err != nil { + return nil, err + } + p.AddStage(osbuild.NewUsersStage(options)) + } + + if groups := c.GetGroups(); len(groups) > 0 { + p.AddStage(osbuild.NewGroupsStage(t.groupStageOptions(groups))) + } + + if services := c.GetServices(); services != nil || t.imageType.enabledServices != nil { + p.AddStage(osbuild.NewSystemdStage(t.systemdStageOptions(t.imageType.enabledServices, t.imageType.disabledServices, services, t.imageType.defaultTarget))) + } + + if firewall := c.GetFirewall(); firewall != nil { + p.AddStage(osbuild.NewFirewallStage(t.firewallStageOptions(firewall))) + } + + p.AddStage(osbuild.NewSELinuxStage(t.selinuxStageOptions())) + + p.Assembler = t.imageType.assembler(t.arch.arch.uefi, size) + + return p, nil +} + +func (r *rhel83ImageType) buildPipeline(repos []rpmmd.RepoConfig, arch arch, buildPackageSpecs []rpmmd.PackageSpec) *osbuild.Pipeline { + p := &osbuild.Pipeline{} + p.AddStage(osbuild.NewRPMStage(r.rpmStageOptions(arch, repos, buildPackageSpecs))) + return p +} + +func (r *rhel83ImageType) rpmStageOptions(arch arch, repos []rpmmd.RepoConfig, specs []rpmmd.PackageSpec) *osbuild.RPMStageOptions { + var gpgKeys []string + for _, repo := range repos { + if repo.GPGKey == "" { + continue + } + gpgKeys = append(gpgKeys, repo.GPGKey) + } + + var packages []string + for _, spec := range specs { + packages = append(packages, spec.Checksum) + } + + return &osbuild.RPMStageOptions{ + GPGKeys: gpgKeys, + Packages: packages, + } +} +func (r *rhel83ImageType) userStageOptions(users []blueprint.UserCustomization) (*osbuild.UsersStageOptions, error) { + options := osbuild.UsersStageOptions{ + Users: make(map[string]osbuild.UsersStageOptionsUser), + } + + for _, c := range users { + if c.Password != nil && !crypt.PasswordIsCrypted(*c.Password) { + cryptedPassword, err := crypt.CryptSHA512(*c.Password) + if err != nil { + return nil, err + } + + c.Password = &cryptedPassword + } + + user := osbuild.UsersStageOptionsUser{ + Groups: c.Groups, + Description: c.Description, + Home: c.Home, + Shell: c.Shell, + Password: c.Password, + Key: c.Key, + } + + if c.UID != nil { + uid := strconv.Itoa(*c.UID) + user.UID = &uid + } + + if c.GID != nil { + gid := strconv.Itoa(*c.GID) + user.GID = &gid + } + + options.Users[c.Name] = user + } + + return &options, nil +} + +func (r *rhel83ImageType) groupStageOptions(groups []blueprint.GroupCustomization) *osbuild.GroupsStageOptions { + options := osbuild.GroupsStageOptions{ + Groups: map[string]osbuild.GroupsStageOptionsGroup{}, + } + + for _, group := range groups { + groupData := osbuild.GroupsStageOptionsGroup{ + Name: group.Name, + } + if group.GID != nil { + gid := strconv.Itoa(*group.GID) + groupData.GID = &gid + } + + options.Groups[group.Name] = groupData + } + + return &options +} + +func (r *rhel83ImageType) firewallStageOptions(firewall *blueprint.FirewallCustomization) *osbuild.FirewallStageOptions { + options := osbuild.FirewallStageOptions{ + Ports: firewall.Ports, + } + + if firewall.Services != nil { + options.EnabledServices = firewall.Services.Enabled + options.DisabledServices = firewall.Services.Disabled + } + + return &options +} + +func (r *rhel83ImageType) systemdStageOptions(enabledServices, disabledServices []string, s *blueprint.ServicesCustomization, target string) *osbuild.SystemdStageOptions { + if s != nil { + enabledServices = append(enabledServices, s.Enabled...) + disabledServices = append(disabledServices, s.Disabled...) + } + return &osbuild.SystemdStageOptions{ + EnabledServices: enabledServices, + DisabledServices: disabledServices, + DefaultTarget: target, + } +} + +func (r *rhel83ImageType) fsTabStageOptions(uefi bool) *osbuild.FSTabStageOptions { + options := osbuild.FSTabStageOptions{} + options.AddFilesystem("0bd700f8-090f-4556-b797-b340297ea1bd", "xfs", "/", "defaults", 0, 0) + if uefi { + options.AddFilesystem("46BB-8120", "vfat", "/boot/efi", "umask=0077,shortname=winnt", 0, 2) + } + return &options +} + +func (r *rhel83ImageType) grub2StageOptions(kernelOptions string, uefi bool) *osbuild.GRUB2StageOptions { + id := uuid.MustParse("0bd700f8-090f-4556-b797-b340297ea1bd") + + var uefiOptions *osbuild.GRUB2UEFI + if uefi { + uefiOptions = &osbuild.GRUB2UEFI{ + Vendor: "redhat", + } + } + + return &osbuild.GRUB2StageOptions{ + RootFilesystemUUID: id, + KernelOptions: kernelOptions, + Legacy: !uefi, + UEFI: uefiOptions, + } +} + +func (r *rhel83ImageType) selinuxStageOptions() *osbuild.SELinuxStageOptions { + return &osbuild.SELinuxStageOptions{ + FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", + } +} + +func (r *RHEL83) qemuAssembler(format string, filename string, uefi bool, size uint64) *osbuild.Assembler { + var options osbuild.QEMUAssemblerOptions + if uefi { + fstype := uuid.MustParse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") + options = osbuild.QEMUAssemblerOptions{ + Format: format, + Filename: filename, + Size: size, + PTUUID: "8DFDFF87-C96E-EA48-A3A6-9408F1F6B1EF", + PTType: "gpt", + Partitions: []osbuild.QEMUPartition{ + { + Start: 2048, + Size: 972800, + Type: &fstype, + Filesystem: osbuild.QEMUFilesystem{ + Type: "vfat", + UUID: "46BB-8120", + Label: "EFI System Partition", + Mountpoint: "/boot/efi", + }, + }, + { + Start: 976896, + Filesystem: osbuild.QEMUFilesystem{ + Type: "xfs", + UUID: "0bd700f8-090f-4556-b797-b340297ea1bd", + Mountpoint: "/", + }, + }, + }, + } + } else { + options = osbuild.QEMUAssemblerOptions{ + Format: format, + Filename: filename, + Size: size, + PTUUID: "0x14fc63d2", + PTType: "mbr", + Partitions: []osbuild.QEMUPartition{ + { + Start: 2048, + Bootable: true, + Filesystem: osbuild.QEMUFilesystem{ + Type: "xfs", + UUID: "0bd700f8-090f-4556-b797-b340297ea1bd", + Mountpoint: "/", + }, + }, + }, + } + } + return osbuild.NewQEMUAssembler(&options) +} + +func (r *RHEL83) tarAssembler(filename, compression string) *osbuild.Assembler { + return osbuild.NewTarAssembler( + &osbuild.TarAssemblerOptions{ + Filename: filename, + Compression: compression, + }) +} + +func (r *RHEL83) rawFSAssembler(filename string, size uint64) *osbuild.Assembler { + id := uuid.MustParse("0bd700f8-090f-4556-b797-b340297ea1bd") + return osbuild.NewRawFSAssembler( + &osbuild.RawFSAssemblerOptions{ + Filename: filename, + RootFilesystemUUID: id, + Size: size, + FilesystemType: "xfs", + }) +} diff --git a/internal/distro/rhel83/distro_test.go b/internal/distro/rhel83/distro_test.go new file mode 100644 index 000000000..86cf8ff80 --- /dev/null +++ b/internal/distro/rhel83/distro_test.go @@ -0,0 +1,95 @@ +package rhel83_test + +import ( + "testing" + + "github.com/osbuild/osbuild-composer/internal/distro/rhel83" +) + +func TestFilenameFromType(t *testing.T) { + type args struct { + outputFormat string + } + tests := []struct { + name string + args args + want string + want1 string + wantErr bool + }{ + { + name: "ami", + args: args{"ami"}, + want: "image.raw.xz", + want1: "application/octet-stream", + }, + { + name: "ext4", + args: args{"ext4-filesystem"}, + want: "filesystem.img", + want1: "application/octet-stream", + }, + { + name: "openstack", + args: args{"openstack"}, + want: "disk.qcow2", + want1: "application/x-qemu-disk", + }, + { + name: "partitioned-disk", + args: args{"partitioned-disk"}, + want: "disk.img", + want1: "application/octet-stream", + }, + { + name: "qcow2", + args: args{"qcow2"}, + want: "disk.qcow2", + want1: "application/x-qemu-disk", + }, + { + name: "tar", + args: args{"tar"}, + want: "root.tar.xz", + want1: "application/x-tar", + }, + { + name: "vhd", + args: args{"vhd"}, + want: "disk.vhd", + want1: "application/x-vhd", + }, + { + name: "vmdk", + args: args{"vmdk"}, + want: "disk.vmdk", + want1: "application/x-vmdk", + }, + { + name: "invalid-output-type", + args: args{"foobar"}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dist := rhel83.New() + arch, _ := dist.GetArch("x86_64") + imgType, err := arch.GetImageType(tt.args.outputFormat) + if (err != nil) != tt.wantErr { + t.Errorf("Arch.GetImageType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + got := imgType.Filename() + got1 := imgType.MIMEType() + if got != tt.want { + t.Errorf("ImageType.Filename() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("ImageType.MIMEType() got1 = %v, want %v", got1, tt.want1) + } + } + }) + } +} diff --git a/repositories/rhel-8.3.json b/repositories/rhel-8.3.json new file mode 100644 index 000000000..079579664 --- /dev/null +++ b/repositories/rhel-8.3.json @@ -0,0 +1,26 @@ +{ + "aarch64": [ + { + "id": "baseos", + "baseurl": "http://download.devel.redhat.com/rhel-8/rel-eng/RHEL-8/latest-RHEL-8.3/compose/BaseOS/aarch64/os", + "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK\nCRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC\n2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf\nC/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5\nun3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E\n0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE\nIGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh\n8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL\nGht5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki\nJUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25\nOFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq\ndzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==\n=zbHE\n-----END PGP PUBLIC KEY BLOCK-----\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----\n" + }, + { + "id": "appstream", + "baseurl": "http://download.devel.redhat.com/rhel-8/rel-eng/RHEL-8/latest-RHEL-8.3/compose/AppStream/aarch64/os", + "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK\nCRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC\n2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf\nC/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5\nun3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E\n0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE\nIGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh\n8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL\nGht5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki\nJUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25\nOFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq\ndzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==\n=zbHE\n-----END PGP PUBLIC KEY BLOCK-----\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----\n" + } + ], + "x86_64": [ + { + "id": "baseos", + "baseurl": "http://download.devel.redhat.com/rhel-8/rel-eng/RHEL-8/latest-RHEL-8.3/compose/BaseOS/x86_64/os", + "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK\nCRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC\n2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf\nC/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5\nun3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E\n0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE\nIGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh\n8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL\nGht5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki\nJUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25\nOFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq\ndzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==\n=zbHE\n-----END PGP PUBLIC KEY BLOCK-----\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----\n" + }, + { + "id": "appstream", + "baseurl": "http://download.devel.redhat.com/rhel-8/rel-eng/RHEL-8/latest-RHEL-8.3/compose/AppStream/x86_64/os", + "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK\nCRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC\n2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf\nC/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5\nun3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E\n0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE\nIGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh\n8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL\nGht5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki\nJUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25\nOFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq\ndzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==\n=zbHE\n-----END PGP PUBLIC KEY BLOCK-----\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----\n" + } + ] +}