From 042f2ff3c3331ae6066262bef2841a8a2ddc7291 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 11 Oct 2023 18:12:39 +0200 Subject: [PATCH] Improve test coverage Signed-off-by: David Cassany --- pkg/action/build-disk.go | 4 +- pkg/action/build_test.go | 100 ++++++++++++++++++++++++++++ pkg/cloudinit/cloudinit_test.go | 67 ++++++++++++++++++- pkg/elemental/elemental.go | 2 +- pkg/partitioner/disk.go | 26 ++------ pkg/partitioner/options.go | 7 ++ pkg/partitioner/partitioner.go | 10 ++- pkg/partitioner/partitioner_test.go | 8 +-- 8 files changed, 190 insertions(+), 34 deletions(-) diff --git a/pkg/action/build-disk.go b/pkg/action/build-disk.go index e2d0ba9fe69..8b4ad39944a 100644 --- a/pkg/action/build-disk.go +++ b/pkg/action/build-disk.go @@ -140,7 +140,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo if !b.spec.Active.Source.IsEmpty() { // Create active root activeRoot = filepath.Join(workdir, filepath.Base(b.spec.Active.File)+rootSuffix) - activeInfo, err = e.DumpSource(recRoot, b.spec.Active.Source) + activeInfo, err = e.DumpSource(activeRoot, b.spec.Active.Source) if err != nil { b.cfg.Logger.Errorf("failed loading active image source tree: %s", err.Error()) return err @@ -566,7 +566,7 @@ func (b *BuildDiskAction) CreateDiskPartitionTable(disk string) error { var secSize, startS, sizeS uint var excludes v1.PartitionList - gd, _ := partitioner.NewPartitioner(disk, b.cfg.Runner, partitioner.Gdisk) + gd := partitioner.NewPartitioner(disk, b.cfg.Runner, partitioner.Gdisk) dData, err := gd.Print() if err != nil { return err diff --git a/pkg/action/build_test.go b/pkg/action/build_test.go index 4cfb2bcfcf8..cb4dcce173b 100644 --- a/pkg/action/build_test.go +++ b/pkg/action/build_test.go @@ -295,6 +295,61 @@ var _ = Describe("Build Actions", func() { {"mkfs.ext2", "-d", "/tmp/test/build/recovery.img.root", "/tmp/test/build/state/cOS/passive.img"}, {"mksquashfs", "/tmp/test/build/recovery.img.root", "/tmp/test/build/recovery/cOS/recovery.img"}, {"mkfs.vfat", "-n", "COS_GRUB"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI", "::EFI"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/boot", "::EFI/boot"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/elemental", "::EFI/elemental"}, + {"mkfs.ext4", "-L", "COS_OEM"}, + {"mkfs.ext4", "-L", "COS_RECOVERY"}, + {"mkfs.ext4", "-L", "COS_STATE"}, + {"mkfs.ext4", "-L", "COS_PERSISTENT"}, + {"sgdisk", "-p", "/tmp/test/elemental.raw"}, + {"partprobe", "/tmp/test/elemental.raw"}, + })).To(Succeed()) + }) + It("Successfully builds a full raw disk with an unprivileged setup and a different active image", func() { + disk.Unprivileged = true + disk.Active.Source = v1.NewDockerSrc("some/other/image/ref:tag") + disk.Active.FS = constants.LinuxImgFs + disk.Passive.FS = constants.LinuxImgFs + buildDisk := action.NewBuildDiskAction(cfg, disk) + // Unprivileged setup, it should not run any mount + mounter.ErrorOnMount = true + + // grub artifacts are expected to be found in active root + activeRoot := filepath.Join(cfg.OutDir, "build", filepath.Base(disk.Active.File)+".root") + + // Create grub.cfg + grubConf := filepath.Join(activeRoot, "/etc/cos/grub.cfg") + Expect(utils.MkdirAll(fs, filepath.Dir(grubConf), constants.DirPerm)).To(Succeed()) + Expect(fs.WriteFile(grubConf, []byte{}, constants.FilePerm)).To(Succeed()) + + // Create grub modules + grubModulesDir := filepath.Join(activeRoot, "/usr/share/grub2/x86_64-efi") + Expect(utils.MkdirAll(fs, grubModulesDir, constants.DirPerm)).To(Succeed()) + Expect(fs.WriteFile(filepath.Join(grubModulesDir, "loopback.mod"), []byte{}, constants.FilePerm)).To(Succeed()) + Expect(fs.WriteFile(filepath.Join(grubModulesDir, "squash4.mod"), []byte{}, constants.FilePerm)).To(Succeed()) + Expect(fs.WriteFile(filepath.Join(grubModulesDir, "xzio.mod"), []byte{}, constants.FilePerm)).To(Succeed()) + + // Create os-release + Expect(fs.WriteFile(filepath.Join(activeRoot, "/etc/os-release"), []byte{}, constants.FilePerm)).To(Succeed()) + + // Create efi files + grubEfiDir := filepath.Join(activeRoot, "/usr/share/efi/x86_64") + Expect(utils.MkdirAll(fs, grubEfiDir, constants.DirPerm)).To(Succeed()) + Expect(fs.WriteFile(filepath.Join(grubEfiDir, "grub.efi"), []byte{}, constants.FilePerm)) + Expect(fs.WriteFile(filepath.Join(grubEfiDir, "shim.efi"), []byte{}, constants.FilePerm)) + Expect(fs.WriteFile(filepath.Join(grubEfiDir, "MokManager.efi"), []byte{}, constants.FilePerm)) + + Expect(buildDisk.BuildDiskRun()).To(Succeed()) + + Expect(runner.MatchMilestones([][]string{ + {"mkfs.ext2", "-d", "/tmp/test/build/active.img.root", "/tmp/test/build/state/cOS/active.img"}, + {"mkfs.ext2", "-d", "/tmp/test/build/active.img.root", "/tmp/test/build/state/cOS/passive.img"}, + {"mksquashfs", "/tmp/test/build/recovery.img.root", "/tmp/test/build/recovery/cOS/recovery.img"}, + {"mkfs.vfat", "-n", "COS_GRUB"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI", "::EFI"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/boot", "::EFI/boot"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/elemental", "::EFI/elemental"}, {"mkfs.ext4", "-L", "COS_OEM"}, {"mkfs.ext4", "-L", "COS_RECOVERY"}, {"mkfs.ext4", "-L", "COS_STATE"}, @@ -318,6 +373,9 @@ var _ = Describe("Build Actions", func() { Expect(runner.MatchMilestones([][]string{ {"mksquashfs", "/tmp/test/build/recovery.img.root", "/tmp/test/build/recovery/cOS/recovery.img"}, {"mkfs.vfat", "-n", "COS_GRUB"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI", "::EFI"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/boot", "::EFI/boot"}, + {"mcopy", "-i", "/tmp/test/build/efi.part", "/tmp/test/build/efi/EFI/elemental", "::EFI/elemental"}, {"mkfs.ext4", "-L", "COS_OEM"}, {"mkfs.ext4", "-L", "COS_RECOVERY"}, {"mkfs.ext4", "-L", "COS_STATE"}, @@ -325,6 +383,48 @@ var _ = Describe("Build Actions", func() { {"partprobe", "/tmp/test/elemental.raw"}, })).To(Succeed()) }) + It("Fails to build an expandable disk with privileged setup when mounts are not possible", func() { + disk.Unprivileged = false + disk.Expandable = true + disk.Active.FS = constants.LinuxImgFs + disk.Passive.FS = constants.LinuxImgFs + buildDisk := action.NewBuildDiskAction(cfg, disk) + + // build will fail if mounts are not possible + mounter.ErrorOnMount = true + + Expect(buildDisk.BuildDiskRun()).NotTo(Succeed()) + + Expect(runner.MatchMilestones([][]string{ + {"grub2-editenv", "/tmp/test/build/oem/grubenv", "set", "next_entry=recovery"}, + })).To(Succeed()) + + // fails at chroot hook step, before any preparing images + Expect(runner.MatchMilestones([][]string{ + {"mksquashfs", "/tmp/test/build/recovery.img.root", "/tmp/test/build/recovery/cOS/recovery.img"}, + })).NotTo(Succeed()) + }) + It("Fails to build an expandable disk if expandable cloud config cannot be written", func() { + disk.Unprivileged = true + disk.Expandable = true + disk.Active.FS = constants.LinuxImgFs + disk.Passive.FS = constants.LinuxImgFs + buildDisk := action.NewBuildDiskAction(cfg, disk) + + // fails to render the expandable cloud-config data + cloudInit.RenderErr = true + + Expect(buildDisk.BuildDiskRun()).NotTo(Succeed()) + + Expect(runner.MatchMilestones([][]string{ + {"mksquashfs", "/tmp/test/build/recovery.img.root", "/tmp/test/build/recovery/cOS/recovery.img"}, + })).To(Succeed()) + + // failed before preparing partitions images + Expect(runner.MatchMilestones([][]string{ + {"mkfs.vfat", "-n", "COS_GRUB"}, + })).NotTo(Succeed()) + }) It("Transforms raw image into GCE image", Label("gce"), func() { tmpDir, err := utils.TempDir(fs, "", "") defer fs.RemoveAll(tmpDir) diff --git a/pkg/cloudinit/cloudinit_test.go b/pkg/cloudinit/cloudinit_test.go index 73c016d9270..df45b206be7 100644 --- a/pkg/cloudinit/cloudinit_test.go +++ b/pkg/cloudinit/cloudinit_test.go @@ -19,12 +19,14 @@ package cloudinit_test import ( "errors" "fmt" - "io/ioutil" + "io" "log" "os" "github.com/jaypipes/ghw/pkg/block" + "github.com/mudler/yip/pkg/schema" + "github.com/twpayne/go-vfs" "github.com/twpayne/go-vfs/vfst" . "github.com/rancher/elemental-toolkit/pkg/cloudinit" @@ -89,7 +91,7 @@ stages: file, err := os.Open(temp + "/tmp/test/bar") Expect(err).ShouldNot(HaveOccurred()) - b, err := ioutil.ReadAll(file) + b, err := io.ReadAll(file) if err != nil { log.Fatal(err) } @@ -97,6 +99,67 @@ stages: Expect(string(b)).Should(Equal("baz")) }) }) + Describe("writing yaml files", func() { + var fs *vfst.TestFS + var logger v1.Logger + var cleanup func() + var err error + var yipRunner *YipCloudInitRunner + var tempDir string + + BeforeEach(func() { + logger = v1.NewNullLogger() + fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{}) + Expect(err).Should(BeNil()) + yipRunner = NewYipCloudInitRunner(logger, &v1.RealRunner{}, fs) + tempDir = fs.TempDir() + }) + + AfterEach(func() { + cleanup() + }) + + It("produces yaml files and is capable to executed them", func() { + conf := &schema.YipConfig{ + Name: "Example cloud-config file", + Stages: map[string][]schema.Stage{ + "hello-test": { + schema.Stage{ + Name: "Some step", + Commands: []string{ + "echo 'Hello world' > " + tempDir + "/output/hello", + }, + }, + }, + }, + } + Expect(utils.MkdirAll(fs, "/output", constants.DirPerm)).To(Succeed()) + Expect(yipRunner.CloudInitFileRender("/conf/exmaple.yaml", conf)).To(Succeed()) + Expect(yipRunner.Run("hello-test", "/conf")).To(Succeed()) + data, err := fs.ReadFile("/output/hello") + Expect(err).To(BeNil()) + Expect(string(data)).To(Equal("Hello world\n")) + }) + It("fails writing a file on read only filesystem", func() { + conf := &schema.YipConfig{ + Name: "Example cloud-config file", + Stages: map[string][]schema.Stage{ + "hello-test": { + schema.Stage{ + Name: "Some step", + Commands: []string{ + "echo 'Hello world' > " + tempDir + "/output/hello", + }, + }, + }, + }, + } + Expect(utils.MkdirAll(fs, "/output", constants.DirPerm)).To(Succeed()) + roFS := vfs.NewReadOnlyFS(fs) + yipRunner = NewYipCloudInitRunner(logger, &v1.RealRunner{}, roFS) + Expect(yipRunner.CloudInitFileRender("/conf/exmaple.yaml", conf)).NotTo(Succeed()) + }) + }) Describe("layout plugin execution", func() { var runner *v1mock.FakeRunner var afs *vfst.TestFS diff --git a/pkg/elemental/elemental.go b/pkg/elemental/elemental.go index 23eef94d30e..4955395c6c1 100644 --- a/pkg/elemental/elemental.go +++ b/pkg/elemental/elemental.go @@ -361,7 +361,7 @@ func (e *Elemental) CreateImgFromTree(root string, img *v1.Image, noMount bool, e.config.Logger.Infof("Creating squashed image: %s", img.File) err = utils.MkdirAll(e.config.Fs, filepath.Dir(img.File), cnst.DirPerm) if err != nil { - e.config.Logger.Errorf("Filed creating destination folder: %s", err.Error()) + e.config.Logger.Errorf("failed creating destination folder: %s", err.Error()) return err } squashOptions := append(cnst.GetDefaultSquashfsOptions(), e.config.SquashFsCompressionConfig...) diff --git a/pkg/partitioner/disk.go b/pkg/partitioner/disk.go index 6b035da35c3..94fea32a0a8 100644 --- a/pkg/partitioner/disk.go +++ b/pkg/partitioner/disk.go @@ -118,10 +118,7 @@ func (dev *Disk) Exists() bool { } func (dev *Disk) Reload() error { - pc, err := NewPartitioner(dev.String(), dev.runner, dev.partBackend) - if err != nil { - return err - } + pc := NewPartitioner(dev.String(), dev.runner, dev.partBackend) prnt, err := pc.Print() if err != nil { @@ -212,12 +209,9 @@ func (dev Disk) computeFreeSpaceWithoutLast() uint { } func (dev *Disk) NewPartitionTable(label string) (string, error) { - pc, err := NewPartitioner(dev.String(), dev.runner, dev.partBackend) - if err != nil { - return "", err - } + pc := NewPartitioner(dev.String(), dev.runner, dev.partBackend) - err = pc.SetPartitionTableLabel(label) + err := pc.SetPartitionTableLabel(label) if err != nil { return "", err } @@ -237,10 +231,7 @@ func (dev *Disk) NewPartitionTable(label string) (string, error) { // AddPartition adds a partition. Size is expressed in MiB here // Size is expressed in MiB here func (dev *Disk) AddPartition(size uint, fileSystem string, pLabel string, flags ...string) (int, error) { - pc, err := NewPartitioner(dev.String(), dev.runner, dev.partBackend) - if err != nil { - return 0, err - } + pc := NewPartitioner(dev.String(), dev.runner, dev.partBackend) //Check we have loaded partition table data if dev.sectorS == 0 { @@ -251,7 +242,7 @@ func (dev *Disk) AddPartition(size uint, fileSystem string, pLabel string, flags } } - err = pc.SetPartitionTableLabel(dev.label) + err := pc.SetPartitionTableLabel(dev.label) if err != nil { return 0, err } @@ -342,10 +333,7 @@ func (dev Disk) FindPartitionDevice(partNum int) (string, error) { // ExpandLastPartition expands the latest partition in the disk. Size is expressed in MiB here // Size is expressed in MiB here func (dev *Disk) ExpandLastPartition(size uint) (string, error) { - pc, err := NewPartitioner(dev.String(), dev.runner, dev.partBackend) - if err != nil { - return "", err - } + pc := NewPartitioner(dev.String(), dev.runner, dev.partBackend) //Check we have loaded partition table data if dev.sectorS == 0 { @@ -356,7 +344,7 @@ func (dev *Disk) ExpandLastPartition(size uint) (string, error) { } } - err = pc.SetPartitionTableLabel(dev.label) + err := pc.SetPartitionTableLabel(dev.label) if err != nil { return "", err } diff --git a/pkg/partitioner/options.go b/pkg/partitioner/options.go index f61c0c7e308..97d55ce0c39 100644 --- a/pkg/partitioner/options.go +++ b/pkg/partitioner/options.go @@ -42,3 +42,10 @@ func WithLogger(logger v1.Logger) func(d *Disk) error { return nil } } + +func WithGdisk() func(d *Disk) error { + return func(d *Disk) error { + d.partBackend = Gdisk + return nil + } +} diff --git a/pkg/partitioner/partitioner.go b/pkg/partitioner/partitioner.go index cb2e09b717a..e551920fd47 100644 --- a/pkg/partitioner/partitioner.go +++ b/pkg/partitioner/partitioner.go @@ -17,8 +17,6 @@ limitations under the License. package partitioner import ( - "fmt" - v1 "github.com/rancher/elemental-toolkit/pkg/types/v1" ) @@ -49,13 +47,13 @@ type Partition struct { FileSystem string } -func NewPartitioner(dev string, runner v1.Runner, backend string) (Partitioner, error) { +func NewPartitioner(dev string, runner v1.Runner, backend string) Partitioner { switch backend { case Parted: - return newPartedCall(dev, runner), nil + return newPartedCall(dev, runner) case Gdisk: - return newGdiskCall(dev, runner), nil + return newGdiskCall(dev, runner) default: - return nil, fmt.Errorf("unsupported partitioning utility") + return nil } } diff --git a/pkg/partitioner/partitioner_test.go b/pkg/partitioner/partitioner_test.go index 10c8d22a4b6..4a2ef9ee9f9 100644 --- a/pkg/partitioner/partitioner_test.go +++ b/pkg/partitioner/partitioner_test.go @@ -67,10 +67,10 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func( Describe("Gdisk tests", Label("sgdisk"), func() { var gc part.Partitioner BeforeEach(func() { - gc, _ = part.NewPartitioner("/dev/device", runner, part.Gdisk) + gc = part.NewPartitioner("/dev/device", runner, part.Gdisk) }) It("Write changes does nothing with empty setup", func() { - gc, _ := part.NewPartitioner("/dev/device", runner, part.Gdisk) + gc := part.NewPartitioner("/dev/device", runner, part.Gdisk) _, err := gc.WriteChanges() Expect(err).To(BeNil()) }) @@ -189,10 +189,10 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func( Describe("Parted tests", Label("parted"), func() { var pc part.Partitioner BeforeEach(func() { - pc, _ = part.NewPartitioner("/dev/device", runner, part.Parted) + pc = part.NewPartitioner("/dev/device", runner, part.Parted) }) It("Write changes does nothing with empty setup", func() { - pc, _ := part.NewPartitioner("/dev/device", runner, part.Parted) + pc := part.NewPartitioner("/dev/device", runner, part.Parted) _, err := pc.WriteChanges() Expect(err).To(BeNil()) })