diff --git a/pkg/action/build-disk.go b/pkg/action/build-disk.go index 2e00fe2d6f2..311a7b9bebc 100644 --- a/pkg/action/build-disk.go +++ b/pkg/action/build-disk.go @@ -153,7 +153,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo activeRoot := recRoot // Create recovery root - recInfo, err = e.DumpSource(recRoot, b.spec.Recovery.Source) + recInfo, err = elemental.DumpSource(b.cfg.Config, recRoot, b.spec.Recovery.Source) if err != nil { b.cfg.Logger.Errorf("failed loading recovery image source tree: %s", err.Error()) return err @@ -165,7 +165,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(activeRoot, b.spec.Active.Source) + activeInfo, err = elemental.DumpSource(b.cfg.Config, activeRoot, b.spec.Active.Source) if err != nil { b.cfg.Logger.Errorf("failed loading active image source tree: %s", err.Error()) return err @@ -174,7 +174,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo } // Copy cloud-init if any - err = e.CopyCloudConfig(b.roots[constants.OEMPartName], b.spec.CloudInit) + err = elemental.CopyCloudConfig(b.cfg.Config, b.roots[constants.OEMPartName], b.spec.CloudInit) if err != nil { return elementalError.NewFromError(err, elementalError.CopyFile) } @@ -378,7 +378,7 @@ func (b *BuildDiskAction) CreatePartitionImages(e *elemental.Elemental) ([]*v1.I b.cfg.Logger.Infof("Creating EFI partition image") img = b.spec.Partitions.EFI.ToImage() - err = e.CreateFileSystemImage(img) + err = elemental.CreateFileSystemImage(b.cfg.Config, img, "", false) if err != nil { b.cfg.Logger.Errorf("failed creating EFI image: %s", err.Error()) return nil, err diff --git a/pkg/action/build-iso.go b/pkg/action/build-iso.go index 8fe40511e15..8d01484b3c7 100644 --- a/pkg/action/build-iso.go +++ b/pkg/action/build-iso.go @@ -251,12 +251,12 @@ func (b BuildISOAction) createEFI(root string, img string) error { align := int64(4 * 1024 * 1024) efiSizeMB := (efiSize/align*align + align) / (1024 * 1024) - err = b.e.CreateFileSystemImage(&v1.Image{ + err = elemental.CreateFileSystemImage(b.cfg.Config, &v1.Image{ File: img, Size: uint(efiSizeMB), FS: constants.EfiFs, Label: constants.EfiLabel, - }) + }, "", false) if err != nil { return err } @@ -329,7 +329,7 @@ func (b BuildISOAction) burnISO(root, efiImg string) error { func (b BuildISOAction) applySources(target string, sources ...*v1.ImageSource) error { for _, src := range sources { - _, err := b.e.DumpSource(target, src) + _, err := elemental.DumpSource(b.cfg.Config, target, src) if err != nil { return elementalError.NewFromError(err, elementalError.DumpSource) } diff --git a/pkg/action/install.go b/pkg/action/install.go index 0331c9f2d22..b9d671491d9 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -155,11 +155,12 @@ func (i InstallAction) Run() (err error) { // Set installation sources from a downloaded ISO if i.spec.Iso != "" { - isoCleaner, err := e.UpdateSourceFormISO(i.spec.Iso, &i.spec.Active) + isoSrc, isoCleaner, err := elemental.SourceFormISO(i.cfg.Config, i.spec.Iso) cleanup.Push(isoCleaner) if err != nil { return elementalError.NewFromError(err, elementalError.Unknown) } + i.spec.Active.Source = isoSrc } // Partition and format device if needed @@ -190,7 +191,7 @@ func (i InstallAction) Run() (err error) { cleanup.Push(func() error { return treeCleaner() }) // Copy cloud-init if any - err = e.CopyCloudConfig(i.spec.Partitions.GetConfigStorage(), i.spec.CloudInit) + err = elemental.CopyCloudConfig(i.cfg.Config, i.spec.Partitions.GetConfigStorage(), i.spec.CloudInit) if err != nil { return elementalError.NewFromError(err, elementalError.CopyFile) } diff --git a/pkg/action/reset.go b/pkg/action/reset.go index ae997e901e4..10cfa8ebf7d 100644 --- a/pkg/action/reset.go +++ b/pkg/action/reset.go @@ -198,7 +198,7 @@ func (r ResetAction) Run() (err error) { cleanup.Push(func() error { return treeCleaner() }) // Copy cloud-init if any - err = e.CopyCloudConfig(r.spec.Partitions.GetConfigStorage(), r.spec.CloudInit) + err = elemental.CopyCloudConfig(r.cfg.Config, r.spec.Partitions.GetConfigStorage(), r.spec.CloudInit) if err != nil { return elementalError.NewFromError(err, elementalError.CopyFile) } diff --git a/pkg/elemental/elemental.go b/pkg/elemental/elemental.go index 4528abea601..e5ed750ea0a 100644 --- a/pkg/elemental/elemental.go +++ b/pkg/elemental/elemental.go @@ -210,84 +210,90 @@ func (e Elemental) UnmountPartition(part *v1.Partition) error { return e.config.Mounter.Unmount(part.MountPoint) } -// MountImage mounts an image with the given mount options -func (e Elemental) MountImage(img *v1.Image, opts ...string) error { - e.config.Logger.Debugf("Mounting image %s to %s", img.Label, img.MountPoint) - err := utils.MkdirAll(e.config.Fs, img.MountPoint, cnst.DirPerm) +// MountFileSystemImage mounts an image with the given mount options +func MountFileSystemImage(c v1.Config, img *v1.Image, opts ...string) error { + c.Logger.Debugf("Mounting image %s to %s", img.Label, img.MountPoint) + err := utils.MkdirAll(c.Fs, img.MountPoint, cnst.DirPerm) if err != nil { - e.config.Logger.Errorf("Failed creating mountpoint %s", img.MountPoint) + c.Logger.Errorf("Failed creating mountpoint %s", img.MountPoint) return err } - out, err := e.config.Runner.Run("losetup", "--show", "-f", img.File) + out, err := c.Runner.Run("losetup", "--show", "-f", img.File) if err != nil { - e.config.Logger.Errorf("Failed setting a loop device for %s", img.File) + c.Logger.Errorf("Failed setting a loop device for %s", img.File) return err } loop := strings.TrimSpace(string(out)) - err = e.config.Mounter.Mount(loop, img.MountPoint, "auto", opts) + err = c.Mounter.Mount(loop, img.MountPoint, "auto", opts) if err != nil { - e.config.Logger.Errorf("Failed to mount %s", loop) - _, _ = e.config.Runner.Run("losetup", "-d", loop) + c.Logger.Errorf("Failed to mount %s", loop) + _, _ = c.Runner.Run("losetup", "-d", loop) return err } img.LoopDevice = loop return nil } -// UnmountImage unmounts the given image or does nothing if not mounted -func (e Elemental) UnmountImage(img *v1.Image) error { +// UnmountFilesystemImage unmounts the given image or does nothing if not mounted +func UnmountFileSystemImage(c v1.Config, img *v1.Image) error { // Using IsLikelyNotMountPoint seams to be safe as we are not checking // for bind mounts here - if notMnt, _ := e.config.Mounter.IsLikelyNotMountPoint(img.MountPoint); notMnt { - e.config.Logger.Debugf("Not unmounting image, %s doesn't look like mountpoint", img.MountPoint) + if notMnt, _ := c.Mounter.IsLikelyNotMountPoint(img.MountPoint); notMnt { + c.Logger.Debugf("Not unmounting image, %s doesn't look like mountpoint", img.MountPoint) return nil } - e.config.Logger.Debugf("Unmounting image %s from %s", img.Label, img.MountPoint) - err := e.config.Mounter.Unmount(img.MountPoint) + c.Logger.Debugf("Unmounting image %s from %s", img.Label, img.MountPoint) + err := c.Mounter.Unmount(img.MountPoint) if err != nil { return err } - _, err = e.config.Runner.Run("losetup", "-d", img.LoopDevice) + _, err = c.Runner.Run("losetup", "-d", img.LoopDevice) img.LoopDevice = "" return err } -// CreateFileSystemImage creates the image file for the given image -func (e Elemental) CreateFileSystemImage(img *v1.Image) error { - return e.CreatePreLoadedFileSystemImage(img, "") -} - -// CreatePreLoadedFileSystemImage creates the image file for the given image including the contents of the rootDir. -// If rootDir is empty it simply creates an empty filesystem image -func (e Elemental) CreatePreLoadedFileSystemImage(img *v1.Image, rootDir string) error { - e.config.Logger.Infof("Creating filesystem image %s with size: %d", img.File, img.Size) - err := utils.MkdirAll(e.config.Fs, filepath.Dir(img.File), cnst.DirPerm) +// CreateFileSystemImage creates the image file for the given image. An root tree path +// can be used to determine the image size and the preload flag can be used to create an image +// including the root tree data. +func CreateFileSystemImage(c v1.Config, img *v1.Image, rootDir string, preload bool) error { + c.Logger.Infof("Creating image %s", img.File) + err := utils.MkdirAll(c.Fs, filepath.Dir(img.File), cnst.DirPerm) if err != nil { + c.Logger.Errorf("failed creating directory for %s", img.File) return err } - err = utils.CreateRAWFile(e.config.Fs, img.File, img.Size) + if img.Size == 0 && rootDir != "" { + size, err := utils.DirSizeMB(c.Fs, rootDir) + if err != nil { + return err + } + img.Size = size + cnst.ImgOverhead + c.Logger.Debugf("Image size %dM", img.Size) + } + + err = utils.CreateRAWFile(c.Fs, img.File, img.Size) if err != nil { + c.Logger.Errorf("failed creating raw file %s", img.File) return err } - var extraOpts []string - - // Only add the rootDir if it's not empty - match, _ := regexp.MatchString("ext[2-4]", img.FS) - exists, _ := utils.Exists(e.config.Fs, rootDir) - if !match && exists { - e.config.Logger.Infof("Pre-loaded image creation is only available for ext[2-4] filesystems, ignoring options for %s", img.FS) - } - if exists && match { + extraOpts := []string{} + r := regexp.MustCompile("ext[2-4]") + match := r.MatchString(img.FS) + if preload && match { extraOpts = []string{"-d", rootDir} } - - mkfs := partitioner.NewMkfsCall(img.File, img.FS, img.Label, e.config.Runner, extraOpts...) + if preload && !match { + c.Logger.Errorf("Preloaded filesystem images are only supported for ext2-4 filesystems") + return fmt.Errorf("unexpected filesystem: %s", img.FS) + } + mkfs := partitioner.NewMkfsCall(img.File, img.FS, img.Label, c.Runner, extraOpts...) _, err = mkfs.Apply() if err != nil { - _ = e.config.Fs.RemoveAll(img.File) + c.Logger.Errorf("failed formatting file %s with %s", img.File, img.FS) + _ = c.Fs.RemoveAll(img.File) return err } return nil @@ -327,7 +333,7 @@ func (e *Elemental) DeployImgTree(img *v1.Image, root string) (info interface{}, return e.config.Fs.RemoveAll(tmp) } - info, err = e.DumpSource(root, img.Source) + info, err = DumpSource(*e.config, root, img.Source) if err != nil { _ = cleaner() return nil, nil, err @@ -341,61 +347,46 @@ func (e *Elemental) DeployImgTree(img *v1.Image, root string) (info interface{}, return info, cleaner, err } -// CreateImgFromTree creates the given image from with the contents of the tree for the given root. -// NoMount flag allows formatting an image including its contents (experimental and ext* specific) -func (e *Elemental) CreateImgFromTree(root string, img *v1.Image, noMount bool, cleaner func() error) (err error) { - if cleaner != nil { - defer func() { - cErr := cleaner() - if cErr != nil && err == nil { - err = cErr - } - }() - } - - var preLoadRoot string - if noMount { - preLoadRoot = root - } - +// CreateImageFromTree creates the given image including the given root tree. If preload flag is true +// it attempts to preload the root tree at filesystem format time. This allows creating images with the +// given root tree without the need of mounting them. +func CreateImageFromTree(c v1.Config, img *v1.Image, rootDir string, preload bool) (err error) { if img.FS == cnst.SquashFs { - e.config.Logger.Infof("Creating squashed image: %s", img.File) - err = utils.MkdirAll(e.config.Fs, filepath.Dir(img.File), cnst.DirPerm) + c.Logger.Infof("Creating squashfs image for file %s", img.File) + + err = utils.MkdirAll(c.Fs, filepath.Dir(img.File), cnst.DirPerm) if err != nil { - e.config.Logger.Errorf("failed creating destination folder: %s", err.Error()) + c.Logger.Errorf("failed creating directories for %s: %v", img.File, err) return err } - squashOptions := append(cnst.GetDefaultSquashfsOptions(), e.config.SquashFsCompressionConfig...) - err = utils.CreateSquashFS(e.config.Runner, e.config.Logger, root, img.File, squashOptions) + + squashOptions := append(cnst.GetDefaultSquashfsOptions(), c.SquashFsCompressionConfig...) + err = utils.CreateSquashFS(c.Runner, c.Logger, rootDir, img.File, squashOptions) if err != nil { + c.Logger.Errorf("failed creating squashfs image for %s: %v", img.File, err) return err } } else { - if img.Size == 0 { - size, err := utils.DirSizeMB(e.config.Fs, root) - if err != nil { - return err - } - img.Size = size + cnst.ImgOverhead - } - err = e.CreatePreLoadedFileSystemImage(img, preLoadRoot) + err = CreateFileSystemImage(c, img, rootDir, preload) if err != nil { + c.Logger.Errorf("failed creating filesystem image: %v", err) return err } - - if !noMount { - err = e.MountImage(img, "rw") + if !preload { + err = MountFileSystemImage(c, img, "rw") if err != nil { + c.Logger.Errorf("failed mounting filesystem image: %v", err) return err } defer func() { - mErr := e.UnmountImage(img) + mErr := UnmountFileSystemImage(c, img) if err == nil && mErr != nil { err = mErr } }() - e.config.Logger.Infof("Sync %s to %s", root, img.MountPoint) - err = utils.SyncData(e.config.Logger, e.config.Runner, e.config.Fs, root, img.MountPoint) + + c.Logger.Infof("Sync %s to %s", rootDir, img.MountPoint) + err = utils.SyncData(c.Logger, c.Runner, c.Fs, rootDir, img.MountPoint) if err != nil { return err } @@ -404,6 +395,22 @@ func (e *Elemental) CreateImgFromTree(root string, img *v1.Image, noMount bool, return err } +// CreateImgFromTree creates the given image from with the contents of the tree for the given root. +// NoMount flag allows formatting an image including its contents (experimental and ext* specific) +func (e *Elemental) CreateImgFromTree(root string, img *v1.Image, noMount bool, cleaner func() error) (err error) { + if cleaner != nil { + defer func() { + cErr := cleaner() + if cErr != nil && err == nil { + err = cErr + } + }() + } + + err = CreateImageFromTree(*e.config, img, root, noMount) + return err +} + // CopyFileImg copies the files target as the source of this image. It also applies the img label over the copied image. func (e *Elemental) CopyFileImg(img *v1.Image) error { if !img.Source.IsFile() { @@ -445,77 +452,77 @@ func (e *Elemental) DeployImage(img *v1.Image) (interface{}, error) { } // DumpSource sets the image data according to the image source type -func (e *Elemental) DumpSource(target string, imgSrc *v1.ImageSource) (info interface{}, err error) { // nolint:gocyclo - e.config.Logger.Infof("Copying %s source...", imgSrc.Value()) +func DumpSource(c v1.Config, target string, imgSrc *v1.ImageSource) (info interface{}, err error) { // nolint:gocyclo + c.Logger.Infof("Copying %s source...", imgSrc.Value()) - err = utils.MkdirAll(e.config.Fs, target, cnst.DirPerm) + err = utils.MkdirAll(c.Fs, target, cnst.DirPerm) if err != nil { - e.config.Logger.Errorf("failed to create target directory %s", target) + c.Logger.Errorf("failed to create target directory %s", target) return nil, err } if imgSrc.IsImage() { - if e.config.Cosign { - e.config.Logger.Infof("Running cosing verification for %s", imgSrc.Value()) + if c.Cosign { + c.Logger.Infof("Running cosing verification for %s", imgSrc.Value()) out, err := utils.CosignVerify( - e.config.Fs, e.config.Runner, imgSrc.Value(), - e.config.CosignPubKey, v1.IsDebugLevel(e.config.Logger), + c.Fs, c.Runner, imgSrc.Value(), + c.CosignPubKey, v1.IsDebugLevel(c.Logger), ) if err != nil { - e.config.Logger.Errorf("Cosign verification failed: %s", out) + c.Logger.Errorf("Cosign verification failed: %s", out) return nil, err } } - err = e.config.ImageExtractor.ExtractImage(imgSrc.Value(), target, e.config.Platform.String(), e.config.LocalImage) + err = c.ImageExtractor.ExtractImage(imgSrc.Value(), target, c.Platform.String(), c.LocalImage) if err != nil { return nil, err } } else if imgSrc.IsDir() { excludes := []string{"/mnt", "/proc", "/sys", "/dev", "/tmp", "/host", "/run"} - err = utils.SyncData(e.config.Logger, e.config.Runner, e.config.Fs, imgSrc.Value(), target, excludes...) + err = utils.SyncData(c.Logger, c.Runner, c.Fs, imgSrc.Value(), target, excludes...) if err != nil { return nil, err } } else if imgSrc.IsFile() { - err = utils.MkdirAll(e.config.Fs, cnst.ImgSrcDir, cnst.DirPerm) + err = utils.MkdirAll(c.Fs, cnst.ImgSrcDir, cnst.DirPerm) if err != nil { return nil, err } img := &v1.Image{File: imgSrc.Value(), MountPoint: cnst.ImgSrcDir} - err = e.MountImage(img, "auto", "ro") + err = MountFileSystemImage(c, img, "auto", "ro") if err != nil { return nil, err } - defer e.UnmountImage(img) // nolint:errcheck + defer UnmountFileSystemImage(c, img) // nolint:errcheck excludes := []string{"/mnt", "/proc", "/sys", "/dev", "/tmp", "/host", "/run"} - err = utils.SyncData(e.config.Logger, e.config.Runner, e.config.Fs, cnst.ImgSrcDir, target, excludes...) + err = utils.SyncData(c.Logger, c.Runner, c.Fs, cnst.ImgSrcDir, target, excludes...) if err != nil { return nil, err } } else { return nil, fmt.Errorf("unknown image source type") } - e.config.Logger.Infof("Finished copying %s into %s", imgSrc.Value(), target) + c.Logger.Infof("Finished copying %s into %s", imgSrc.Value(), target) return info, nil } // CopyCloudConfig will check if there is a cloud init in the config and store it on the target -func (e *Elemental) CopyCloudConfig(path string, cloudInit []string) (err error) { +func CopyCloudConfig(c v1.Config, path string, cloudInit []string) (err error) { if path == "" { - e.config.Logger.Warnf("empty path. Will not copy cloud config files.") + c.Logger.Warnf("empty path. Will not copy cloud config files.") return nil } for i, ci := range cloudInit { customConfig := filepath.Join(path, fmt.Sprintf("9%d_custom.yaml", i)) - err = utils.GetSource(e.config, ci, customConfig) + err = utils.GetSource(c, ci, customConfig) if err != nil { return err } - if err = e.config.Fs.Chmod(customConfig, cnst.FilePerm); err != nil { + if err = c.Fs.Chmod(customConfig, cnst.FilePerm); err != nil { return err } - e.config.Logger.Infof("Finished copying cloud config file %s to %s", cloudInit, customConfig) + c.Logger.Infof("Finished copying cloud config file %s to %s", cloudInit, customConfig) } return nil } @@ -559,38 +566,38 @@ func (e *Elemental) CheckActiveDeployment(labels []string) bool { return false } -// UpdateSourceISO downloads an ISO in a temporary folder, mounts it and updates active image to use the ISO squashfs image as -// source. Returns a cleaner method to unmount and remove the temporary folder afterwards. -func (e Elemental) UpdateSourceFormISO(iso string, activeImg *v1.Image) (func() error, error) { +// SourceISO downloads an ISO in a temporary folder, mounts it and returns the image source to be used +// Returns a source and cleaner method to unmount and remove the temporary folder afterwards. +func SourceFormISO(c v1.Config, iso string) (*v1.ImageSource, func() error, error) { nilErr := func() error { return nil } - tmpDir, err := utils.TempDir(e.config.Fs, "", "elemental") + tmpDir, err := utils.TempDir(c.Fs, "", "elemental") if err != nil { - return nilErr, err + return nil, nilErr, err } - cleanTmpDir := func() error { return e.config.Fs.RemoveAll(tmpDir) } + cleanTmpDir := func() error { return c.Fs.RemoveAll(tmpDir) } tmpFile := filepath.Join(tmpDir, "elemental.iso") - err = utils.GetSource(e.config, iso, tmpFile) + err = utils.GetSource(c, iso, tmpFile) if err != nil { - return cleanTmpDir, err + return nil, cleanTmpDir, err } isoMnt := filepath.Join(tmpDir, "iso") - err = utils.MkdirAll(e.config.Fs, isoMnt, cnst.DirPerm) + err = utils.MkdirAll(c.Fs, isoMnt, cnst.DirPerm) if err != nil { - return cleanTmpDir, err + return nil, cleanTmpDir, err } - e.config.Logger.Infof("Mounting iso %s into %s", tmpFile, isoMnt) - err = e.config.Mounter.Mount(tmpFile, isoMnt, "auto", []string{"loop"}) + c.Logger.Infof("Mounting iso %s into %s", tmpFile, isoMnt) + err = c.Mounter.Mount(tmpFile, isoMnt, "auto", []string{"loop"}) if err != nil { - return cleanTmpDir, err + return nil, cleanTmpDir, err } cleanAll := func() error { - cErr := e.config.Mounter.Unmount(isoMnt) + cErr := c.Mounter.Unmount(isoMnt) if cErr != nil { return cErr } @@ -598,13 +605,12 @@ func (e Elemental) UpdateSourceFormISO(iso string, activeImg *v1.Image) (func() } squashfsImg := filepath.Join(isoMnt, cnst.ISORootFile) - ok, _ := utils.Exists(e.config.Fs, squashfsImg) + ok, _ := utils.Exists(c.Fs, squashfsImg) if !ok { - return cleanAll, fmt.Errorf("squashfs image not found in ISO: %s", squashfsImg) + return nil, cleanAll, fmt.Errorf("squashfs image not found in ISO: %s", squashfsImg) } - activeImg.Source = v1.NewFileSrc(squashfsImg) - return cleanAll, nil + return v1.NewFileSrc(squashfsImg), cleanAll, nil } // DeactivateDevice deactivates unmounted the block devices present within the system. diff --git a/pkg/elemental/elemental_test.go b/pkg/elemental/elemental_test.go index 95c55aaae64..df3c978a3e9 100644 --- a/pkg/elemental/elemental_test.go +++ b/pkg/elemental/elemental_test.go @@ -225,62 +225,57 @@ var _ = Describe("Elemental", Label("elemental"), func() { }) Describe("MountImage", Label("MountImage", "mount", "image"), func() { - var el *elemental.Elemental var img *v1.Image BeforeEach(func() { - el = elemental.NewElemental(config) img = &v1.Image{MountPoint: "/some/mountpoint"} }) It("Mounts file system image", func() { runner.ReturnValue = []byte("/dev/loop") - Expect(el.MountImage(img)).To(BeNil()) + Expect(elemental.MountFileSystemImage(*config, img)).To(BeNil()) Expect(img.LoopDevice).To(Equal("/dev/loop")) }) It("Fails to set a loop device", Label("loop"), func() { runner.ReturnError = errors.New("failed to set a loop device") - Expect(el.MountImage(img)).NotTo(BeNil()) + Expect(elemental.MountFileSystemImage(*config, img)).NotTo(BeNil()) Expect(img.LoopDevice).To(Equal("")) }) It("Fails to mount a loop device", Label("loop"), func() { runner.ReturnValue = []byte("/dev/loop") mounter.ErrorOnMount = true - Expect(el.MountImage(img)).NotTo(BeNil()) + Expect(elemental.MountFileSystemImage(*config, img)).NotTo(BeNil()) Expect(img.LoopDevice).To(Equal("")) }) }) Describe("UnmountImage", Label("UnmountImage", "mount", "image"), func() { - var el *elemental.Elemental var img *v1.Image BeforeEach(func() { runner.ReturnValue = []byte("/dev/loop") - el = elemental.NewElemental(config) img = &v1.Image{MountPoint: "/some/mountpoint"} - Expect(el.MountImage(img)).To(BeNil()) + Expect(elemental.MountFileSystemImage(*config, img)).To(BeNil()) Expect(img.LoopDevice).To(Equal("/dev/loop")) }) It("Unmounts file system image", func() { - Expect(el.UnmountImage(img)).To(BeNil()) + Expect(elemental.UnmountFileSystemImage(*config, img)).To(BeNil()) Expect(img.LoopDevice).To(Equal("")) }) It("Fails to unmount a mountpoint", func() { mounter.ErrorOnUnmount = true - Expect(el.UnmountImage(img)).NotTo(BeNil()) + Expect(elemental.UnmountFileSystemImage(*config, img)).NotTo(BeNil()) }) It("Fails to unset a loop device", Label("loop"), func() { runner.ReturnError = errors.New("failed to unset a loop device") - Expect(el.UnmountImage(img)).NotTo(BeNil()) + Expect(elemental.UnmountFileSystemImage(*config, img)).NotTo(BeNil()) }) }) Describe("CreateFileSystemImage", Label("CreateFileSystemImage", "image"), func() { - var el *elemental.Elemental var img *v1.Image BeforeEach(func() { img = &v1.Image{ @@ -292,13 +287,12 @@ var _ = Describe("Elemental", Label("elemental"), func() { Source: v1.NewDirSrc(constants.ISOBaseTree), } _ = utils.MkdirAll(fs, constants.ISOBaseTree, constants.DirPerm) - el = elemental.NewElemental(config) }) It("Creates a new file system image", func() { _, err := fs.Stat(img.File) Expect(err).NotTo(BeNil()) - err = el.CreateFileSystemImage(img) + err = elemental.CreateFileSystemImage(*config, img, "", false) Expect(err).To(BeNil()) stat, err := fs.Stat(img.File) Expect(err).To(BeNil()) @@ -309,7 +303,7 @@ var _ = Describe("Elemental", Label("elemental"), func() { runner.ReturnError = errors.New("run error") _, err := fs.Stat(img.File) Expect(err).NotTo(BeNil()) - err = el.CreateFileSystemImage(img) + err = elemental.CreateFileSystemImage(*config, img, "", false) Expect(err).NotTo(BeNil()) _, err = fs.Stat(img.File) Expect(err).NotTo(BeNil()) @@ -481,11 +475,9 @@ var _ = Describe("Elemental", Label("elemental"), func() { }) }) Describe("DumpSource", Label("dump"), func() { - var e *elemental.Elemental var destDir string BeforeEach(func() { var err error - e = elemental.NewElemental(config) destDir, err = utils.TempDir(fs, "", "elemental") Expect(err).ShouldNot(HaveOccurred()) }) @@ -504,46 +496,46 @@ var _ = Describe("Elemental", Label("elemental"), func() { return []byte{}, nil } - _, err := e.DumpSource("/dest", v1.NewDirSrc("/source")) + _, err := elemental.DumpSource(*config, "/dest", v1.NewDirSrc("/source")) Expect(err).ShouldNot(HaveOccurred()) Expect(rsyncCount).To(Equal(1)) Expect(src).To(HaveSuffix("/source/")) Expect(dest).To(HaveSuffix("/dest/")) }) It("Unpacks a docker image to target", Label("docker"), func() { - _, err := e.DumpSource(destDir, v1.NewDockerSrc("docker/image:latest")) + _, err := elemental.DumpSource(*config, destDir, v1.NewDockerSrc("docker/image:latest")) Expect(err).To(BeNil()) }) It("Unpacks a docker image to target with cosign validation", Label("docker", "cosign"), func() { config.Cosign = true - _, err := e.DumpSource(destDir, v1.NewDockerSrc("docker/image:latest")) + _, err := elemental.DumpSource(*config, destDir, v1.NewDockerSrc("docker/image:latest")) Expect(err).To(BeNil()) Expect(runner.CmdsMatch([][]string{{"cosign", "verify", "docker/image:latest"}})) }) It("Fails cosign validation", Label("cosign"), func() { runner.ReturnError = errors.New("cosign error") config.Cosign = true - _, err := e.DumpSource(destDir, v1.NewDockerSrc("docker/image:latest")) + _, err := elemental.DumpSource(*config, destDir, v1.NewDockerSrc("docker/image:latest")) Expect(err).NotTo(BeNil()) Expect(runner.CmdsMatch([][]string{{"cosign", "verify", "docker/image:latest"}})) }) It("Fails to unpack a docker image to target", Label("docker"), func() { unpackErr := errors.New("failed to unpack") extractor.SideEffect = func(_, _, _ string, _ bool) error { return unpackErr } - _, err := e.DumpSource(destDir, v1.NewDockerSrc("docker/image:latest")) + _, err := elemental.DumpSource(*config, destDir, v1.NewDockerSrc("docker/image:latest")) Expect(err).To(Equal(unpackErr)) }) It("Copies image file to target", func() { sourceImg := "/source.img" destFile := filepath.Join(destDir, "active.img") - _, err := e.DumpSource(destFile, v1.NewFileSrc(sourceImg)) + _, err := elemental.DumpSource(*config, destFile, v1.NewFileSrc(sourceImg)) Expect(err).To(BeNil()) Expect(runner.IncludesCmds([][]string{{"rsync"}})) }) It("Fails to copy, source can't be mounted", func() { mounter.ErrorOnMount = true - _, err := e.DumpSource("whatever", v1.NewFileSrc("/source.img")) + _, err := elemental.DumpSource(*config, "whatever", v1.NewFileSrc("/source.img")) Expect(err).To(HaveOccurred()) }) It("Fails to copy, no write permissions", func() { @@ -551,7 +543,7 @@ var _ = Describe("Elemental", Label("elemental"), func() { _, err := fs.Create(sourceImg) Expect(err).To(BeNil()) config.Fs = vfs.NewReadOnlyFS(fs) - _, err = e.DumpSource("whatever", v1.NewFileSrc("/source.img")) + _, err = elemental.DumpSource(*config, "whatever", v1.NewFileSrc("/source.img")) Expect(err).To(HaveOccurred()) }) }) @@ -877,12 +869,6 @@ var _ = Describe("Elemental", Label("elemental"), func() { }) }) Describe("GetIso", Label("GetIso", "iso"), func() { - var e *elemental.Elemental - var activeImg *v1.Image - BeforeEach(func() { - activeImg = &v1.Image{} - e = elemental.NewElemental(config) - }) It("Gets the iso, mounts it and updates image source", func() { tmpDir, err := utils.TempDir(fs, "", "elemental-test") Expect(err).To(BeNil()) @@ -895,15 +881,14 @@ var _ = Describe("Elemental", Label("elemental"), func() { Expect(utils.MkdirAll(fs, filepath.Dir(rootfsImg), constants.DirPerm)).To(Succeed()) Expect(fs.WriteFile(rootfsImg, []byte{}, constants.FilePerm)).To(Succeed()) - isoClean, err := e.UpdateSourceFormISO(iso, activeImg) + source, isoClean, err := elemental.SourceFormISO(*config, iso) Expect(err).To(BeNil()) - Expect(activeImg.Source.IsFile()).To(BeTrue()) + Expect(source.IsFile()).To(BeTrue()) Expect(isoClean()).To(Succeed()) }) It("Fails if it cant find the iso", func() { iso := "whatever" - e := elemental.NewElemental(config) - isoClean, err := e.UpdateSourceFormISO(iso, activeImg) + _, isoClean, err := elemental.SourceFormISO(*config, iso) Expect(err).ToNot(BeNil()) Expect(isoClean()).To(Succeed()) }) @@ -915,7 +900,7 @@ var _ = Describe("Elemental", Label("elemental"), func() { iso := fmt.Sprintf("%s/fake.iso", tmpDir) config.Fs = vfs.NewReadOnlyFS(fs) - isoClean, err := e.UpdateSourceFormISO(iso, activeImg) + _, isoClean, err := elemental.SourceFormISO(*config, iso) Expect(err).ToNot(BeNil()) Expect(isoClean()).To(Succeed()) }) @@ -926,18 +911,16 @@ var _ = Describe("Elemental", Label("elemental"), func() { err = fs.WriteFile(fmt.Sprintf("%s/fake.iso", tmpDir), []byte("Hi"), constants.FilePerm) Expect(err).To(BeNil()) iso := fmt.Sprintf("%s/fake.iso", tmpDir) - isoClean, err := e.UpdateSourceFormISO(iso, activeImg) + _, isoClean, err := elemental.SourceFormISO(*config, iso) Expect(err).ToNot(BeNil()) Expect(err.Error()).To(ContainSubstring("mount error")) Expect(isoClean()).To(Succeed()) }) }) Describe("CloudConfig", Label("CloudConfig", "cloud-config"), func() { - var e *elemental.Elemental var parts v1.ElementalPartitions BeforeEach(func() { parts = conf.NewInstallElementalPartitions() - e = elemental.NewElemental(config) }) It("Copies the cloud config file", func() { testString := "In a galaxy far far away..." @@ -946,19 +929,19 @@ var _ = Describe("Elemental", Label("elemental"), func() { Expect(err).To(BeNil()) Expect(err).To(BeNil()) - err = e.CopyCloudConfig(parts.GetConfigStorage(), cloudInit) + err = elemental.CopyCloudConfig(*config, parts.GetConfigStorage(), cloudInit) Expect(err).To(BeNil()) copiedFile, err := fs.ReadFile(fmt.Sprintf("%s/90_custom.yaml", constants.OEMDir)) Expect(err).To(BeNil()) Expect(copiedFile).To(ContainSubstring(testString)) }) It("Doesnt do anything if the config file is not set", func() { - err := e.CopyCloudConfig(parts.GetConfigStorage(), []string{}) + err := elemental.CopyCloudConfig(*config, parts.GetConfigStorage(), []string{}) Expect(err).To(BeNil()) }) It("Doesnt do anything if the OEM partition has no mount point", func() { parts.OEM.MountPoint = "" - err := e.CopyCloudConfig(parts.GetConfigStorage(), []string{}) + err := elemental.CopyCloudConfig(*config, parts.GetConfigStorage(), []string{}) Expect(err).To(BeNil()) }) }) diff --git a/pkg/utils/common.go b/pkg/utils/common.go index 036b0a87100..2cf3d6a4b3f 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -396,7 +396,7 @@ func IsHTTPURI(uri string) (bool, error) { // GetSource copies given source to destination, if source is a local path it simply // copies files, if source is a remote URL it tries to download URL to destination. -func GetSource(config *v1.Config, source string, destination string) error { +func GetSource(config v1.Config, source string, destination string) error { local, err := IsLocalURI(source) if err != nil { config.Logger.Errorf("Not a valid url: %s", source) diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index eb58eb27f93..f7783f3aadd 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -629,24 +629,24 @@ var _ = Describe("Utils", Label("utils"), func() { }) Describe("GetSource", Label("GetSource"), func() { It("Fails on invalid url", func() { - Expect(utils.GetSource(config, "$htt:|//insane.stuff", "/tmp/dest")).NotTo(BeNil()) + Expect(utils.GetSource(*config, "$htt:|//insane.stuff", "/tmp/dest")).NotTo(BeNil()) }) It("Fails on readonly destination", func() { config.Fs = vfs.NewReadOnlyFS(fs) - Expect(utils.GetSource(config, "http://something.org", "/tmp/dest")).NotTo(BeNil()) + Expect(utils.GetSource(*config, "http://something.org", "/tmp/dest")).NotTo(BeNil()) }) It("Fails on non existing local source", func() { - Expect(utils.GetSource(config, "/some/missing/file", "/tmp/dest")).NotTo(BeNil()) + Expect(utils.GetSource(*config, "/some/missing/file", "/tmp/dest")).NotTo(BeNil()) }) It("Fails on http client error", func() { client.Error = true url := "https://missing.io" - Expect(utils.GetSource(config, url, "/tmp/dest")).NotTo(BeNil()) + Expect(utils.GetSource(*config, url, "/tmp/dest")).NotTo(BeNil()) client.WasGetCalledWith(url) }) It("Copies local file to destination", func() { fs.Create("/tmp/file") - Expect(utils.GetSource(config, "file:///tmp/file", "/tmp/dest")).To(BeNil()) + Expect(utils.GetSource(*config, "file:///tmp/file", "/tmp/dest")).To(BeNil()) _, err := fs.Stat("/tmp/dest") Expect(err).To(BeNil()) })