diff --git a/pkg/distro/rhel9/images.go b/pkg/distro/rhel9/images.go index 33632ce3b3..c51e14c9e0 100644 --- a/pkg/distro/rhel9/images.go +++ b/pkg/distro/rhel9/images.go @@ -490,6 +490,10 @@ func edgeRawImage(workload workload.Workload, img.Filename = t.Filename() img.Compression = t.compression + for _, fs := range customizations.GetFilesystems() { + img.CustomFilesystems = append(img.CustomFilesystems, fs.Mountpoint) + } + return img, nil } @@ -546,6 +550,10 @@ func edgeSimplifiedInstallerImage(workload workload.Workload, rawImg.Filename = t.Filename() + for _, fs := range customizations.GetFilesystems() { + rawImg.CustomFilesystems = append(rawImg.CustomFilesystems, fs.Mountpoint) + } + // 92+ only if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" { rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, kopts.Append) diff --git a/pkg/image/ostree_disk.go b/pkg/image/ostree_disk.go index 99ed5a4d00..2228d56477 100644 --- a/pkg/image/ostree_disk.go +++ b/pkg/image/ostree_disk.go @@ -57,6 +57,8 @@ type OSTreeDiskImage struct { // Container buildable tweaks the buildroot to be container friendly, // i.e. to not rely on an installed osbuild-selinux ContainerBuildable bool + + CustomFilesystems []string } func NewOSTreeDiskImageFromCommit(commit ostree.SourceSpec) *OSTreeDiskImage { @@ -107,6 +109,7 @@ func baseRawOstreeImage(img *OSTreeDiskImage, buildPipeline manifest.Build, opts osPipeline.IgnitionPlatform = img.IgnitionPlatform osPipeline.LockRoot = img.LockRoot osPipeline.UseBootupd = opts.useBootupd + osPipeline.CustomFileSystems = img.CustomFilesystems // other image types (e.g. live) pass the workload to the pipeline. if img.Workload != nil { diff --git a/pkg/manifest/ostree_deployment.go b/pkg/manifest/ostree_deployment.go index 3e3afb97f1..e871cb5079 100644 --- a/pkg/manifest/ostree_deployment.go +++ b/pkg/manifest/ostree_deployment.go @@ -74,6 +74,8 @@ type OSTreeDeployment struct { // Use bootupd instead of grub2 as the bootloader UseBootupd bool + + CustomFileSystems []string } // NewOSTreeCommitDeployment creates a pipeline for an ostree deployment from a @@ -353,6 +355,19 @@ func (p *OSTreeDeployment) serialize() osbuild.Pipeline { }, })) + // This will create a custom systemd unit that create + // mountpoints if its not present.This will safeguard + // any ostree deployment which has custom filesystem + // during ostree upgrade. + // issue # https://github.com/osbuild/images/issues/352 + if len(p.CustomFileSystems) != 0 { + serviceName := "osbuild-ostree-mountpoints.service" + stageOption := osbuild.NewSystemdUnitCreateStageOptions(createMountpointService(serviceName, p.CustomFileSystems)) + stageOption.MountOSTree(p.osName, ref, 0) + pipeline.AddStage(stageOption) + p.EnabledServices = append(p.EnabledServices, serviceName) + } + // We enable / disable services below using the systemd stage, but its effect // may be overridden by systemd which may reset enabled / disabled services on // firstboot (which happend on F37+). This behavior, if available, is triggered @@ -481,3 +496,38 @@ func (p *OSTreeDeployment) getInline() []string { return inlineData } + +// Creates systemd unit stage by ingesting the servicename and mount-points +func createMountpointService(serviceName string, mountpoints []string) *osbuild.SystemdUnitCreateStageOptions { + var conditionPathIsDirectory []string + for _, mountpoint := range mountpoints { + conditionPathIsDirectory = append(conditionPathIsDirectory, "|!"+mountpoint) + } + unit := osbuild.Unit{ + Description: "Ensure custom filesystem mountpoints exist", + DefaultDependencies: false, + ConditionPathIsDirectory: conditionPathIsDirectory, + } + service := osbuild.Service{ + Type: osbuild.Oneshot, + RemainAfterExit: true, + //compatibility with composefs, will require transient rootfs to be enabled too. + ExecStartPre: []string{"/bin/sh -c \"if [ -z \"$(grep -Uq composefs /run/ostree-booted)\" ]; then chattr -i /; fi\""}, + ExecStopPost: []string{"/bin/sh -c \"if [ -z \"$(grep -Uq composefs /run/ostree-booted)\" ]; then chattr +i /; fi\""}, + ExecStart: []string{"mkdir -p " + strings.Join(mountpoints[:], " ")}, + } + install := osbuild.Install{ + WantedBy: []string{"local-fs.target"}, + } + options := osbuild.SystemdUnitCreateStageOptions{ + Filename: serviceName, + UnitPath: osbuild.Etc, + UnitType: osbuild.System, + Config: osbuild.SystemdServiceUnit{ + Unit: &unit, + Service: &service, + Install: &install, + }, + } + return &options +}