From 9226cd2086ceaf13d0598695c156bb5471d50187 Mon Sep 17 00:00:00 2001 From: leigh capili Date: Tue, 4 Aug 2020 23:04:01 -0600 Subject: [PATCH 1/3] Refactor ActivateSnapshot to return snapshotDevPath so it is just calculated once --- pkg/dmlegacy/snapshot.go | 11 +++++++---- pkg/dmlegacy/vm.go | 2 +- pkg/operations/remove.go | 2 +- pkg/operations/start.go | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pkg/dmlegacy/snapshot.go b/pkg/dmlegacy/snapshot.go index 86c39fa52..24b9e57f9 100644 --- a/pkg/dmlegacy/snapshot.go +++ b/pkg/dmlegacy/snapshot.go @@ -17,10 +17,11 @@ import ( const snapshotLockFileName = "ignite-snapshot.lock" -// ActivateSnapshot sets up the snapshot with devicemapper so that it is active and can be used -func ActivateSnapshot(vm *api.VM) (err error) { +// ActivateSnapshot sets up the snapshot with devicemapper so that it is active and can be used. +// It returns the path of the bootable snapshot device. +func ActivateSnapshot(vm *api.VM) (devicePath string, err error) { device := util.NewPrefixer().Prefix(vm.GetUID()) - devicePath := vm.SnapshotDev() + devicePath = vm.SnapshotDev() // Return if the snapshot is already setup if util.FileExists(devicePath) { @@ -47,7 +48,8 @@ func ActivateSnapshot(vm *api.VM) (err error) { // Create a lockfile and obtain a lock. lock, err := lockfile.New(glpath) if err != nil { - return fmt.Errorf("failed to create lock: %v", err) + err = fmt.Errorf("failed to create lockfile: %w", err) + return } if err = obtainLock(lock); err != nil { return @@ -105,6 +107,7 @@ func ActivateSnapshot(vm *api.VM) (err error) { // "0 8388608 snapshot /dev/{loop0,mapper/ignite--base} /dev/loop1 P 8" dmTable := []byte(fmt.Sprintf("0 %d snapshot %s %s P 8", overlayLoopSize, basePath, overlayLoop.Path())) + // setup the main boot device if err = runDMSetup(device, dmTable); err != nil { return } diff --git a/pkg/dmlegacy/vm.go b/pkg/dmlegacy/vm.go index b4eb368cb..737835ef3 100644 --- a/pkg/dmlegacy/vm.go +++ b/pkg/dmlegacy/vm.go @@ -83,7 +83,7 @@ func AllocateAndPopulateOverlay(vm *api.VM) error { } func copyToOverlay(vm *api.VM) (err error) { - err = ActivateSnapshot(vm) + _, err = ActivateSnapshot(vm) if err != nil { return } diff --git a/pkg/operations/remove.go b/pkg/operations/remove.go index ba0d94abe..d67f0205f 100644 --- a/pkg/operations/remove.go +++ b/pkg/operations/remove.go @@ -48,7 +48,7 @@ func CleanupVM(vm *api.VM) error { // TODO should this function return a proper error? RemoveVMContainer(inspectResult) - // After remove the VM container, and the SnapshotDev still there + // After removing the VM container, if the Snapshot Device is still there, clean up if _, err := os.Stat(vm.SnapshotDev()); err == nil { // try remove it again with DeactivateSnapshot if err := cleanup.DeactivateSnapshot(vm); err != nil { diff --git a/pkg/operations/start.go b/pkg/operations/start.go index 21edde5af..12cce4ccb 100644 --- a/pkg/operations/start.go +++ b/pkg/operations/start.go @@ -25,7 +25,8 @@ func StartVM(vm *api.VM, debug bool) error { RemoveVMContainer(inspectResult) // Setup the snapshot overlay filesystem - if err := dmlegacy.ActivateSnapshot(vm); err != nil { + snapshotDevPath, err := dmlegacy.ActivateSnapshot(vm) + if err != nil { return err } @@ -70,7 +71,7 @@ func StartVM(vm *api.VM, debug bool) error { runtime.BindBoth("/dev/mapper/control"), // This enables containerized Ignite to remove its own dm snapshot runtime.BindBoth("/dev/net/tun"), // Needed for creating TAP adapters runtime.BindBoth("/dev/kvm"), // Pass through virtualization support - runtime.BindBoth(vm.SnapshotDev()), // The block device to boot from + runtime.BindBoth(snapshotDevPath), // The block device to boot from }, StopTimeout: constants.STOP_TIMEOUT + constants.IGNITE_TIMEOUT, PortBindings: vm.Spec.Network.Ports, // Add the port mappings to Docker From e9ddb462f97e0747f415cc188ed1682e704b493d Mon Sep 17 00:00:00 2001 From: leigh capili Date: Tue, 4 Aug 2020 23:07:10 -0600 Subject: [PATCH 2/3] Fix DeactivateSnapshot bug which would cause cleanup failures --- pkg/dmlegacy/cleanup/deactivate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/dmlegacy/cleanup/deactivate.go b/pkg/dmlegacy/cleanup/deactivate.go index 54fd828ae..ca932c7a2 100644 --- a/pkg/dmlegacy/cleanup/deactivate.go +++ b/pkg/dmlegacy/cleanup/deactivate.go @@ -9,7 +9,7 @@ import ( func DeactivateSnapshot(vm *api.VM) error { dmArgs := []string{ "remove", - vm.SnapshotDev(), + util.NewPrefixer().Prefix(vm.GetUID()), } // If the base device is visible in "dmsetup", we should remove it From 7176fff659b4e9840d7fb858348dc0d5397b24ef Mon Sep 17 00:00:00 2001 From: leigh capili Date: Tue, 4 Aug 2020 23:10:14 -0600 Subject: [PATCH 3/3] Configure dmsetup to fallback to managing device nodes without udevd This patch frees up dependent interactions with udev for device node creation. Ignite can now create boot devices in absence of a working udevd equivalent. This makes ignite easier to run in docker containers and WSL2. --- pkg/dmlegacy/cleanup/deactivate.go | 1 + pkg/dmlegacy/loopdev.go | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/dmlegacy/cleanup/deactivate.go b/pkg/dmlegacy/cleanup/deactivate.go index ca932c7a2..2598e5d12 100644 --- a/pkg/dmlegacy/cleanup/deactivate.go +++ b/pkg/dmlegacy/cleanup/deactivate.go @@ -9,6 +9,7 @@ import ( func DeactivateSnapshot(vm *api.VM) error { dmArgs := []string{ "remove", + "--verifyudev", // if udevd is not running, dmsetup will manage the device node in /dev/mapper util.NewPrefixer().Prefix(vm.GetUID()), } diff --git a/pkg/dmlegacy/loopdev.go b/pkg/dmlegacy/loopdev.go index 59f80e458..2c5e3872d 100644 --- a/pkg/dmlegacy/loopdev.go +++ b/pkg/dmlegacy/loopdev.go @@ -36,7 +36,11 @@ func (ld *loopDevice) Size512K() (uint64, error) { // dmsetup uses stdin to read multiline tables, this is a helper function for that func runDMSetup(name string, table []byte) error { - cmd := exec.Command("dmsetup", "create", name) + cmd := exec.Command( + "dmsetup", "create", + "--verifyudev", // if udevd is not running, dmsetup will manage the device node in /dev/mapper + name, + ) stdin, err := cmd.StdinPipe() if err != nil { return err @@ -52,7 +56,7 @@ func runDMSetup(name string, table []byte) error { out, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("command %q exited with %q: %v", cmd.Args, out, err) + return fmt.Errorf("command %q exited with %q: %w", cmd.Args, out, err) } return nil