Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to switch to snapshotter interface #1906

Merged
merged 11 commits into from
Jan 31, 2024
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ build-disk: build-os
mkdir -p $(ROOT_DIR)/build
$(DOCKER) run --rm -v $(DOCKER_SOCK):$(DOCKER_SOCK) -v $(ROOT_DIR)/build:/build \
--entrypoint /usr/bin/elemental \
$(TOOLKIT_REPO):$(VERSION) --debug build-disk --platform $(PLATFORM) --unprivileged --expandable -n elemental-$(FLAVOR).$(ARCH) --local \
--squash-no-compression -o /build $(REPO):$(VERSION)
$(TOOLKIT_REPO):$(VERSION) --debug build-disk --platform $(PLATFORM) --expandable -n elemental-$(FLAVOR).$(ARCH) --local \
--squash-no-compression -o /build --system $(REPO):$(VERSION)
qemu-img convert -O qcow2 $(ROOT_DIR)/build/elemental-$(FLAVOR).$(ARCH).raw $(ROOT_DIR)/build/elemental-$(FLAVOR).$(ARCH).qcow2
qemu-img resize $(ROOT_DIR)/build/elemental-$(FLAVOR).$(ARCH).qcow2 $(DISKSIZE)

Expand Down
20 changes: 7 additions & 13 deletions cmd/build-disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (

"github.com/rancher/elemental-toolkit/pkg/constants"
eleError "github.com/rancher/elemental-toolkit/pkg/error"
elementalError "github.com/rancher/elemental-toolkit/pkg/error"
v1 "github.com/rancher/elemental-toolkit/pkg/types/v1"

"github.com/spf13/cobra"
Expand All @@ -38,7 +37,7 @@ func NewBuildDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command {
c := &cobra.Command{
Use: "build-disk image",
Short: "Build a disk image using the given image (experimental and subject to change)",
Args: cobra.MaximumNArgs(1),
Args: cobra.ExactArgs(0),
PreRunE: func(cmd *cobra.Command, args []string) error {
if addCheckRoot {
return CheckRoot()
Expand All @@ -48,7 +47,6 @@ func NewBuildDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) (err error) {
var cfg *v1.BuildConfig
var spec *v1.DiskSpec
var imgSource *v1.ImageSource

defer func() {
if cfg != nil && err != nil {
Expand Down Expand Up @@ -83,16 +81,11 @@ func NewBuildDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command {
return eleError.NewFromError(err, eleError.ReadingBuildDiskConfig)
}

if len(args) > 0 {
imgSource, err = v1.NewSrcFromURI(args[0])
if err != nil {
cfg.Logger.Errorf("not a valid image argument: %s", args[0])
return elementalError.NewFromError(err, elementalError.IdentifySource)
}
spec.Recovery.Source = imgSource
builder, err := action.NewBuildDiskAction(cfg, spec)
if err != nil {
cfg.Logger.Errorf("failed to initialize build disk action: %v", err)
return err
}

builder := action.NewBuildDiskAction(cfg, spec)
return builder.BuildDiskRun()
},
}
Expand All @@ -102,11 +95,12 @@ func NewBuildDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command {
c.Flags().StringP("output", "o", "", "Output directory (defaults to current directory)")
c.Flags().Bool("date", false, "Adds a date suffix into the generated disk file")
c.Flags().Bool("expandable", false, "Creates an expandable image including only the recovery image")
c.Flags().Bool("unprivileged", false, "Makes a build runnable within a non-privileged container, avoids mounting filesystems (experimental)")
c.Flags().VarP(imgType, "type", "t", "Type of image to create")
c.Flags().StringSliceP("cloud-init", "c", []string{}, "Cloud-init config files to include in disk")
c.Flags().StringSlice("cloud-init-paths", []string{}, "Cloud-init config files to run during build")
c.Flags().StringSlice("deploy-command", []string{"elemental", "--debug", "reset", "--reboot"}, "Deployment command for expandable images")
addSystemFlag(c)
addRecoverySystemFlag(c)
addPlatformFlags(c)
addLocalImageFlag(c)
addSquashFsCompressionFlags(c)
Expand Down
19 changes: 8 additions & 11 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package config

import (
"errors"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -305,22 +304,20 @@ func applyKernelCmdline(r *v1.RunConfig, mount *v1.MountSpec) error {
}

switch split[0] {
case "elemental.image":
mount.Mode = val
case "elemental.disable", "rd.cos.disable":
mount.Disable = true
case "cos-img/filename":
switch val {
case constants.ActiveImgPath:
case "elemental.image", "cos-img/filename":
switch {
case strings.Contains(val, constants.ActiveImgName):
mount.Mode = constants.ActiveImgName
case constants.PassiveImgPath:
case strings.Contains(val, constants.PassiveImgName):
mount.Mode = constants.PassiveImgName
case constants.RecoveryImgPath:
case strings.Contains(val, constants.RecoveryImgName):
mount.Mode = constants.RecoveryImgName
default:
r.Logger.Errorf("Error parsing cmdline %s", cmd)
return errors.New("Unknown image path")
return fmt.Errorf("Unknown image path: %s", val)
}
case "elemental.disable", "rd.cos.disable":
mount.Disable = true
case "elemental.overlay", "rd.cos.overlay":
err := applyMountOverlay(mount, val)
if err != nil {
Expand Down
29 changes: 16 additions & 13 deletions cmd/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,23 @@ var _ = Describe("Config", Label("config"), func() {
It("reads values correctly", func() {
cfg, err := ReadConfigRun("fixtures/simple/", nil, mounter)
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Snapshotter.MaxSnaps).To(Equal(7), litter.Sdump(cfg))
Expect(cfg.Snapshotter.Type).To(Equal(constants.LoopDeviceSnapshotterType), litter.Sdump(cfg))
loop, ok := cfg.Snapshotter.Config.(*v1.LoopDeviceConfig)
Expect(ok).To(BeTrue())
Expect(loop.Size).To(Equal(uint(2000)))

Expect(cfg.Config.Cosign).To(BeTrue(), litter.Sdump(cfg))

up, err := ReadUpgradeSpec(cfg, nil)
Expect(err).Should(HaveOccurred(), litter.Sdump(cfg))

Expect(up.GrubDefEntry).To(Equal("so"))
Expect(up.Active.Size).To(Equal(uint(2000)), litter.Sdump(up))

inst, err := ReadInstallSpec(cfg, nil)
Expect(err).Should(HaveOccurred(), litter.Sdump(cfg))

Expect(inst.GrubDefEntry).To(Equal("mockme"))
Expect(inst.Active.Size).To(Equal(uint(2000)), litter.Sdump(up))
})
})
})
Expand Down Expand Up @@ -187,9 +190,8 @@ var _ = Describe("Config", Label("config"), func() {
// From config file
Expect(disk.Size).To(Equal(uint(32768)))
Expect(disk.Partitions.OEM.Size).To(Equal(uint(32)))
Expect(disk.Unprivileged).To(BeTrue())
Expect(disk.Expandable).To(BeTrue())
Expect(disk.Recovery.Label).To(BeEmpty())
Expect(disk.RecoverySystem.Label).To(BeEmpty())
})
})
})
Expand Down Expand Up @@ -219,7 +221,6 @@ var _ = Describe("Config", Label("config"), func() {
Expect(cfg.Runner != nil).To(BeTrue())
_, ok := cfg.Runner.(*v1.RealRunner)
Expect(ok).To(BeTrue())
Expect(cfg.Snapshotter.MaxSnaps).To(Equal(constants.MaxSnaps))
})
It("uses provided configs and flags, flags have priority", func() {
cfg, err := ReadConfigRun("fixtures/config/", flags, mounter)
Expand Down Expand Up @@ -249,10 +250,12 @@ var _ = Describe("Config", Label("config"), func() {
It("reads the snaphotter configuration", func() {
// Default value
cfg, err := ReadConfigRun("fixtures/config/", nil, mounter)
Expect(err).ShouldNot(HaveOccurred())

Expect(err).To(BeNil())
Expect(cfg.Snapshotter.Type).To(Equal(constants.LoopDeviceSnapshotterType))
Expect(cfg.Snapshotter.MaxSnaps).To(Equal(7))
snapshooterCfg, ok := cfg.Snapshotter.Config.(v1.LoopDeviceConfig)
snapshooterCfg, ok := cfg.Snapshotter.Config.(*v1.LoopDeviceConfig)
Expect(ok).To(BeTrue())
Expect(snapshooterCfg.FS).To(Equal("xfs"))
Expect(snapshooterCfg.Size).To(Equal(uint(1024)))
Expand Down Expand Up @@ -307,8 +310,8 @@ var _ = Describe("Config", Label("config"), func() {

BeforeEach(func() {
flags = pflag.NewFlagSet("testflags", 1)
flags.String("system.uri", "", "testing flag")
flags.Set("system.uri", "docker:image/from:flag")
flags.String("system", "", "testing flag")
flags.Set("system", "docker:image/from:flag")
})
It("inits a default install spec if no configs are provided", func() {
spec, err := ReadInstallSpec(cfg, nil)
Expand All @@ -333,11 +336,11 @@ var _ = Describe("Config", Label("config"), func() {
// Overwrites target from environment variables
Expect(spec.Target == "/env/disk")
// Overwrites system image, flags have priority over files and env vars
Expect(spec.Active.Source.Value() == "image/from:flag")
Expect(spec.System.Value() == "image/from:flag")
// Overwerites default value for DisableBootEntry from an env var
Expect(spec.DisableBootEntry).To(BeTrue())
// Uses recovery and no-format defined in confing.yaml
Expect(spec.Recovery.Source.Value() == "recovery/image:latest")
Expect(spec.RecoverySystem.Source.Value() == "recovery/image:latest")
Expect(spec.NoFormat == true)
// Gets multiple cloud-init files from env vars as comma separated values
Expect(len(spec.CloudInit)).To(Equal(2))
Expand Down Expand Up @@ -411,7 +414,7 @@ var _ = Describe("Config", Label("config"), func() {
Expect(spec.CloudInit[0]).To(Equal("path/to/file1.yaml"))
Expect(spec.CloudInit[1]).To(Equal("/absolute/path/to/file2.yaml"))
// Overwrites system image, flags have priority over files and env vars
Expect(spec.Active.Source.Value() == "image/from:flag")
Expect(spec.System.Value() == "image/from:flag")
// From config files
Expect(spec.DisableBootEntry).To(BeTrue())
// From env vars
Expand Down Expand Up @@ -459,9 +462,9 @@ var _ = Describe("Config", Label("config"), func() {
spec, err := ReadUpgradeSpec(cfg, nil)
Expect(err).ShouldNot(HaveOccurred())
// Overwrites recovery-system image, flags have priority over files and env vars
Expect(spec.Recovery.Source.Value() == "image/from:flag")
Expect(spec.RecoverySystem.Source.Value() == "image/from:flag")
// System image from config files
Expect(spec.Active.Source.Value() == "system/cos")
Expect(spec.System.Value() == "system/cos")
// Sets recovery upgrade from environment variables
Expect(spec.RecoveryUpgrade).To(BeTrue())
})
Expand Down
8 changes: 3 additions & 5 deletions cmd/config/fixtures/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ install:
target: "someDisk"

no-format: true
system:
uri: docker:some/image:latest
system: docker:some/image:latest
recovery-system:
uri: docker:recovery/image:latest

reset:
disable-boot-entry: true

upgrade:
system:
uri: some/image:latest
system: some/image:latest
recovery-system:
uri: recovery/image:latest

Expand Down
3 changes: 1 addition & 2 deletions cmd/config/fixtures/config/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ disk:
persistent:
size: 0
fs: xfs
unprivileged: true
expandable: true
system: some.registry.org/my/image:mytag
recovery-system:
uri: some.registry.org/my/image:mytag
fs: squashfs
type: raw

Expand Down
11 changes: 6 additions & 5 deletions cmd/config/fixtures/simple/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ verify: true

install:
grub-entry-name: "mockme"
system:
size: 2000
recovery-system:
size: 2000
upgrade:
grub-entry-name: "so"
recovery-system:
size: 2000
system:
size: 2000
reset:
grub-entry-name: "awesome"
system:

snapshotter:
type: loopdevice
max-snaps: 7
config:
fs: xfs
size: 2000
18 changes: 14 additions & 4 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func addPowerFlags(cmd *cobra.Command) {
// addSharedInstallUpgradeFlags add flags shared between install, upgrade and reset
func addSharedInstallUpgradeFlags(cmd *cobra.Command) {
addResetFlags(cmd)
cmd.Flags().String("recovery-system.uri", "", "Sets the recovery image source and its type (e.g. 'docker:registry.org/image:tag')")
addRecoverySystemFlag(cmd)
addSquashFsCompressionFlags(cmd)
}

Expand All @@ -55,21 +55,31 @@ func addResetFlags(cmd *cobra.Command) {
cmd.Flags().StringP("docker-image", "d", "", "Install a specified container image")
_ = cmd.Flags().MarkDeprecated("docker-image", "'docker-image' is deprecated please use 'system' instead")

cmd.Flags().String("system.uri", "", "Sets the system image source and its type (e.g. 'docker:registry.org/image:tag')")
cmd.Flags().Bool("verify", false, "Enable mtree checksum verification (requires images manifests generated with mtree separately)")
cmd.Flags().Bool("strict", false, "Enable strict check of hooks (They need to exit with 0)")

addSystemFlag(cmd)
addCosignFlags(cmd)
addPowerFlags(cmd)
}

// addSystemFlag adds system flag to define source OS
func addSystemFlag(cmd *cobra.Command) {
cmd.Flags().String("system", "", "Sets the system image source and its type (e.g. 'docker:registry.org/image:tag')")
}

// addRecoverySystemFlag adds the recovery-system.uri flag to define recovery source OS
func addRecoverySystemFlag(cmd *cobra.Command) {
cmd.Flags().String("recovery-system.uri", "", "Sets the recovery image source and its type (e.g. 'docker:registry.org/image:tag')")
}

// addLocalImageFlag add local image flag shared between install, pull-image, upgrade
func addLocalImageFlag(cmd *cobra.Command) {
cmd.Flags().Bool("local", false, "Use an image from local cache")
}

func adaptDockerImageAndDirectoryFlagsToSystem(flags *pflag.FlagSet) {
systemFlag := "system.uri"
systemFlag := "system"
doc, _ := flags.GetString("docker-image")
if doc != "" {
_ = flags.Set(systemFlag, fmt.Sprintf("docker:%s", doc))
Expand All @@ -96,7 +106,7 @@ func validateCosignFlags(log v1.Logger, flags *pflag.FlagSet) error {

func validateSourceFlags(_ v1.Logger, flags *pflag.FlagSet) error {
msg := "flags docker-image, directory and system are mutually exclusive, please only set one of them"
system, _ := flags.GetString("system.uri")
system, _ := flags.GetString("system")
directory, _ := flags.GetString("directory")
dockerImg, _ := flags.GetString("docker-image")
// docker-image, directory and system are mutually exclusive. Can't have your cake and eat it too.
Expand Down
6 changes: 5 additions & 1 deletion cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ func NewInstallCmd(root *cobra.Command, addCheckRoot bool) *cobra.Command {
}

cfg.Logger.Infof("Install called")
install := action.NewInstallAction(cfg, spec)
install, err := action.NewInstallAction(cfg, spec)
if err != nil {
cfg.Logger.Errorf("failed to initialize install action: %v", err)
return err
}
return install.Run()
},
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ var _ = Describe("Install", Label("install", "cmd"), func() {
Expect(err.(*elementalError.ElementalError).ExitCode()).To(Equal(elementalError.ReadingInstallUpgradeFlags))
})
It("Errors out if no installation source is defined", Label("args"), func() {
_, _, err := executeCommandC(rootCmd, "install")
_, _, err := executeCommandC(rootCmd, "install", "/dev/nonexisting")
Expect(err).ToNot(BeNil())
Expect(buf.String()).To(ContainSubstring("undefined system source to install"))
})
It("Errors out if no installation target is defined", Label("args"), func() {
_, _, err := executeCommandC(rootCmd, "install", "--directory", "dir")
Expect(err).ToNot(BeNil())
Expect(buf.String()).To(ContainSubstring("at least a target device must be supplied"))
Expect(buf.String()).To(ContainSubstring("target device must be supplied"))
Expect(err.(*elementalError.ElementalError)).ToNot(BeNil())
Expect(err.(*elementalError.ElementalError).ExitCode()).To(Equal(elementalError.InvalidTarget))
})
Expand Down
5 changes: 4 additions & 1 deletion cmd/pull-image.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,15 @@ func NewPullImageCmd(root *cobra.Command, addCheckRoot bool) *cobra.Command {

cfg.Logger.Infof("Pulling image %s platform %s", image, cfg.Platform.String())

var digest string
e := v1.OCIImageExtractor{}
if err = e.ExtractImage(image, destination, cfg.Platform.String(), local); err != nil {
if digest, err = e.ExtractImage(image, destination, cfg.Platform.String(), local); err != nil {
cfg.Logger.Error(err.Error())
return elementalError.NewFromError(err, elementalError.UnpackImage)
}

cfg.Logger.Info("Extracted image digest: %s", digest)

return nil
},
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ func NewResetCmd(root *cobra.Command, addCheckRoot bool) *cobra.Command {
}

cfg.Logger.Infof("Reset called")
reset := action.NewResetAction(cfg, spec)
reset, err := action.NewResetAction(cfg, spec)
if err != nil {
cfg.Logger.Errorf("failed to initialize reset action: %v", err)
return err
}
return reset.Run()
},
}
Expand Down
Loading
Loading