From b1cc863b105843ccb4ab23f174f9f8a6e8408890 Mon Sep 17 00:00:00 2001 From: David Cassany Viladomat Date: Thu, 8 Aug 2024 10:38:52 +0200 Subject: [PATCH] Make RAW disks recovery partition expandable (#2159) Signed-off-by: David Cassany --- pkg/action/build-disk.go | 24 +++++++++++++++++-- pkg/action/build_test.go | 4 ++-- .../system/elemental-setup-pre-rootfs.service | 17 +++++++++++++ .../system/elemental-setup-rootfs.service | 2 -- .../10elemental-setup/module-setup.sh | 6 +++++ .../10elemental-setup/oem-generator.sh | 11 ++++----- .../20elemental-sysroot/sysroot-generator.sh | 8 +++---- pkg/partitioner/disk.go | 5 ++-- pkg/partitioner/partitioner_test.go | 13 +++++++++- pkg/partitioner/sgdisk.go | 8 +++---- 10 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-pre-rootfs.service diff --git a/pkg/action/build-disk.go b/pkg/action/build-disk.go index 011e58ae70c..bdc88077592 100644 --- a/pkg/action/build-disk.go +++ b/pkg/action/build-disk.go @@ -44,6 +44,7 @@ const ( GB = 1024 * MB rootSuffix = ".root" layoutSetStage = "rootfs.before" + expandStage = "pre-rootfs.before" deployStage = "network" postResetHook = "post-reset" cloudinitFile = "00_disk_layout_setup.yaml" @@ -353,6 +354,9 @@ func (b *BuildDiskAction) CreatePartitionImages() ([]*types.Image, error) { for _, part := range []*types.Partition{b.spec.Partitions.OEM, b.spec.Partitions.Recovery} { b.cfg.Logger.Infof("Creating %s partition image", part.Name) img = part.ToImage() + if part.Name == constants.RecoveryPartName && b.spec.Expandable { + img.Size = 0 + } err = elemental.CreateImageFromTree( b.cfg.Config, img, b.roots[part.Name], b.spec.Expandable, func() error { return b.cfg.Fs.RemoveAll(b.roots[part.Name]) }, @@ -674,7 +678,11 @@ func (b *BuildDiskAction) CreateDiskPartitionTable(disk string) error { // reuse startS and SizeS from previous partition startS = startS + sizeS } - sizeS = partitioner.MiBToSectors(part.Size, secSize) + if part.Name == constants.RecoveryPartName && b.spec.Expandable { + sizeS = 0 + } else { + sizeS = partitioner.MiBToSectors(part.Size, secSize) + } var gdPart = partitioner.Partition{ Number: i + 1, StartS: startS, @@ -769,7 +777,19 @@ func (b *BuildDiskAction) SetExpandableCloudInitStage() error { conf := &schema.YipConfig{ Name: "Expand disk layout", Stages: map[string][]schema.Stage{ - layoutSetStage: { + expandStage: { + schema.Stage{ + Name: "Expand recovery", + Layout: schema.Layout{ + Device: &schema.Device{ + Label: b.spec.Partitions.Recovery.FilesystemLabel, + }, + Expand: &schema.Expand{ + Size: b.spec.Partitions.Recovery.Size, + }, + }, + }, + }, layoutSetStage: { schema.Stage{ Name: "Add state partition", Layout: schema.Layout{ diff --git a/pkg/action/build_test.go b/pkg/action/build_test.go index b1ea9f3f80e..16362c0bcf7 100644 --- a/pkg/action/build_test.go +++ b/pkg/action/build_test.go @@ -238,7 +238,7 @@ var _ = Describe("Build Actions", func() { {"losetup", "--show", "-f", "/tmp/test/build/recovery.part"}, {"mkfs.ext4", "-L", "COS_PERSISTENT"}, {"losetup", "--show", "-f", "/tmp/test/build/persistent.part"}, - {"sgdisk", "-p", "/tmp/test/elemental.raw"}, + {"sgdisk", "-p", "-v", "/tmp/test/elemental.raw"}, {"partx", "-u", "/tmp/test/elemental.raw"}, })).To(Succeed()) }) @@ -259,7 +259,7 @@ var _ = Describe("Build Actions", func() { {"mkfs.vfat", "-n", "COS_GRUB"}, {"mkfs.ext4", "-L", "COS_OEM"}, {"mkfs.ext4", "-L", "COS_RECOVERY"}, - {"sgdisk", "-p", "/tmp/test/elemental.raw"}, + {"sgdisk", "-p", "-v", "/tmp/test/elemental.raw"}, {"partx", "-u", "/tmp/test/elemental.raw"}, })).To(Succeed()) }) diff --git a/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-pre-rootfs.service b/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-pre-rootfs.service new file mode 100644 index 00000000000..4b68ee88dfd --- /dev/null +++ b/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-pre-rootfs.service @@ -0,0 +1,17 @@ +[Unit] +Description=Elemental system pre-rootfs setup +DefaultDependencies=no +After=basic.target +Requires=basic.target +Before=initrd-root-device.target +After=oem.mount +Wants=oem.mount +Conflicts=initrd-switch-root.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/elemental run-stage pre-rootfs + +[Install] +WantedBy=initrd-root-device.target \ No newline at end of file diff --git a/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-rootfs.service b/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-rootfs.service index cd3891de674..225f3568184 100644 --- a/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-rootfs.service +++ b/pkg/features/embedded/elemental-setup/etc/systemd/system/elemental-setup-rootfs.service @@ -3,8 +3,6 @@ Description=Elemental system early rootfs setup DefaultDependencies=no After=sysroot.mount Requires=sysroot.mount -After=oem.mount -Wants=oem.mount Before=initrd-root-fs.target Conflicts=initrd-switch-root.target diff --git a/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/module-setup.sh b/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/module-setup.sh index 39c54092a41..07ea8ea1db1 100755 --- a/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/module-setup.sh +++ b/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/module-setup.sh @@ -24,6 +24,12 @@ install() { inst_script "${moddir}/oem-generator.sh" \ "${systemdutildir}/system-generators/dracut-elemental-oem-generator" + inst_simple "/etc/systemd/system/elemental-setup-pre-rootfs.service" \ + "${systemdsystemunitdir}/elemental-setup-pre-rootfs.service" + mkdir -p "${initdir}/${systemdsystemunitdir}/initrd-root-device.target.wants" + ln_r "../elemental-setup-pre-rootfs.service" \ + "${systemdsystemunitdir}/initrd-root-device.target.wants/elemental-setup-pre-rootfs.service" + inst_simple "/etc/systemd/system/elemental-setup-rootfs.service" \ "${systemdsystemunitdir}/elemental-setup-rootfs.service" mkdir -p "${initdir}/${systemdsystemunitdir}/initrd-root-fs.target.wants" diff --git a/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/oem-generator.sh b/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/oem-generator.sh index ee9891a3dc3..b5c22d9cea9 100755 --- a/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/oem-generator.sh +++ b/pkg/features/embedded/elemental-setup/usr/lib/dracut/modules.d/10elemental-setup/oem-generator.sh @@ -21,9 +21,8 @@ if [ -n "${oem_label}" ]; then { echo "[Unit]" echo "DefaultDependencies=no" - echo "Before=elemental-setup-rootfs.service" - echo "After=dracut-initqueue.service" - echo "Wants=dracut-initqueue.service" + echo "After=basic.target" + echo "Wants=basic.target" echo "PartOf=initrd.target" echo "[Mount]" echo "Where=/oem" @@ -34,12 +33,12 @@ if [ -n "${oem_label}" ]; then mkdir -p "$GENERATOR_DIR/$dev.device.d" { echo "[Unit]" - echo "Before=initrd-root-fs.target" + echo "Before=initrd-root-device.target" echo "JobRunningTimeoutSec=${oem_timeout}" } > "$GENERATOR_DIR/$dev.device.d/timeout.conf" - mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.wants + mkdir -p "$GENERATOR_DIR"/initrd-root-device.target.wants ln -s "$GENERATOR_DIR"/"$dev".device \ - "$GENERATOR_DIR"/initrd-root-fs.target.wants/"$dev".device + "$GENERATOR_DIR"/initrd-root-device.target.wants/"$dev".device fi diff --git a/pkg/features/embedded/elemental-sysroot/usr/lib/dracut/modules.d/20elemental-sysroot/sysroot-generator.sh b/pkg/features/embedded/elemental-sysroot/usr/lib/dracut/modules.d/20elemental-sysroot/sysroot-generator.sh index 012e07e1a5b..42d461ac851 100755 --- a/pkg/features/embedded/elemental-sysroot/usr/lib/dracut/modules.d/20elemental-sysroot/sysroot-generator.sh +++ b/pkg/features/embedded/elemental-sysroot/usr/lib/dracut/modules.d/20elemental-sysroot/sysroot-generator.sh @@ -58,8 +58,8 @@ if [ "${snapshotter}" == "btrfs" ]; then echo "[Unit]" echo "Before=initrd-root-fs.target" echo "DefaultDependencies=no" - echo "After=dracut-initqueue.service" - echo "Wants=dracut-initqueue.service" + echo "After=initrd-root-device.target" + echo "Wants=initrd-root-device.target" echo "[Mount]" echo "Where=/sysroot" echo "What=${root}" @@ -112,8 +112,8 @@ else echo "[Unit]" echo "Before=initrd-root-fs.target" echo "DefaultDependencies=no" - echo "After=dracut-initqueue.service" - echo "Wants=dracut-initqueue.service" + echo "After=initrd-root-device.target" + echo "Wants=initrd-root-device.target" echo "[Mount]" echo "Where=${root_part_mnt}" echo "What=${root}" diff --git a/pkg/partitioner/disk.go b/pkg/partitioner/disk.go index b23f31e8830..8b2157445ed 100644 --- a/pkg/partitioner/disk.go +++ b/pkg/partitioner/disk.go @@ -34,10 +34,11 @@ import ( const ( partitionTries = 10 // Parted warning substring for expanded disks without fixing GPT headers - partedWarn = "Not all of the space available" + partedWarn = "Not all of the space available" + sgdiskProblem = "Problem: The secondary header" ) -var unallocatedRegexp = regexp.MustCompile(partedWarn) +var unallocatedRegexp = regexp.MustCompile(fmt.Sprintf("(%s|%s)", partedWarn, sgdiskProblem)) type Disk struct { device string diff --git a/pkg/partitioner/partitioner_test.go b/pkg/partitioner/partitioner_test.go index ea9c4fc1b52..c3adbce9d7a 100644 --- a/pkg/partitioner/partitioner_test.go +++ b/pkg/partitioner/partitioner_test.go @@ -160,7 +160,7 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func( Expect(runner.CmdsMatch(cmds)).To(BeNil()) }) It("Prints partition table info", func() { - cmd := []string{"sgdisk", "-p", "/dev/device"} + cmd := []string{"sgdisk", "-p", "-v", "/dev/device"} _, err := gc.Print() Expect(err).To(BeNil()) Expect(runner.CmdsMatch([][]string{cmd})).To(BeNil()) @@ -408,6 +408,7 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func( Expect(dev.GetLabel()).To(Equal("msdos")) }) It("It fixes GPT headers if the disk was expanded", func() { + // for parted regex runner.ReturnValue = []byte("Warning: Not all of the space available to /dev/loop0...\n" + partedPrint) Expect(dev.Reload()).To(BeNil()) Expect(runner.MatchMilestones([][]string{ @@ -415,6 +416,16 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func( {"sgdisk", "-e", "/dev/device"}, {"parted", "--script", "--machine", "--", "/dev/device", "unit", "s", "print"}, })).To(BeNil()) + // for sgdisk regex + dev = part.NewDisk("/dev/device", part.WithRunner(runner), part.WithFS(fs), part.WithMounter(mounter), part.WithGdisk()) + runner.ReturnValue = []byte(sgdiskPrint + "\nProblem: The secondary header's self-pointer indicates that...\n") + runner.ClearCmds() + Expect(dev.Reload()).To(BeNil()) + Expect(runner.MatchMilestones([][]string{ + {"sgdisk", "-p", "-v", "/dev/device"}, + {"sgdisk", "-e", "/dev/device"}, + {"sgdisk", "-p", "-v", "/dev/device"}, + })).To(BeNil()) }) }) Describe("Modify disk", func() { diff --git a/pkg/partitioner/sgdisk.go b/pkg/partitioner/sgdisk.go index c9099790fa5..3d05af0a654 100644 --- a/pkg/partitioner/sgdisk.go +++ b/pkg/partitioner/sgdisk.go @@ -124,7 +124,7 @@ func (gd *gdiskCall) WriteChanges() (string, error) { func (gd *gdiskCall) SetPartitionTableLabel(label string) error { if label != "gpt" { - return fmt.Errorf("Invalid partition table type (%s), only GPT is supported by sgdisk", label) + return fmt.Errorf("invalid partition table type (%s), only GPT is supported by sgdisk", label) } return nil } @@ -147,7 +147,7 @@ func (gd *gdiskCall) WipeTable(wipe bool) { } func (gd gdiskCall) Print() (string, error) { - out, err := gd.runner.Run("sgdisk", "-p", gd.dev) + out, err := gd.runner.Run("sgdisk", "-p", "-v", gd.dev) return string(out), err } @@ -159,7 +159,7 @@ func (gd gdiskCall) GetLastSector(printOut string) (uint, error) { endS, err := strconv.ParseUint(match[1], 10, 0) return uint(endS), err } - return 0, errors.New("Could not determine last usable sector") + return 0, errors.New("could not determine last usable sector") } // Parses the output of a gdiskCall.Print call @@ -170,7 +170,7 @@ func (gd gdiskCall) GetSectorSize(printOut string) (uint, error) { size, err := strconv.ParseUint(match[1], 10, 0) return uint(size), err } - return 0, errors.New("Could not determine sector size") + return 0, errors.New("could not determine sector size") } // TODO parse printOut from a non gpt disk and return error here