Introduce Podman machine reset

Podman machine reset is a new command that will "reset" your podman
machine environment.  Reset is defined as:

* Stop and Remove all VMs
* Remove the following directories:
    - configuration dir i.e. ~/.config/containers/podman/machine/qemu
    - data dir i.e. ~/.local/.share/containers/podman/machine/qemu

When deleting, if errors are encountered, they will be batched and spit
out at the end.  Podman will try to proceed even in error in doing what
it was told.

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2024-02-14 15:52:58 -06:00
parent fbb4d5dca6
commit 10d748f584
7 changed files with 315 additions and 13 deletions

View File

@ -0,0 +1,98 @@
//go:build amd64 || arm64
package machine
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/pkg/machine"
"github.com/containers/podman/v5/pkg/machine/shim"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/spf13/cobra"
)
var (
resetCmd = &cobra.Command{
Use: "reset [options]",
Short: "Remove all machines",
Long: "Remove all machines, configurations, data, and cached images",
PersistentPreRunE: machinePreRunE,
RunE: reset,
Args: validate.NoArgs,
Example: `podman machine reset`,
ValidArgsFunction: completion.AutocompleteNone,
}
)
var (
resetOptions machine.ResetOptions
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: resetCmd,
Parent: machineCmd,
})
flags := resetCmd.Flags()
formatFlagName := "force"
flags.BoolVarP(&resetOptions.Force, formatFlagName, "f", false, "Do not prompt before reset")
}
func reset(_ *cobra.Command, _ []string) error {
var (
err error
)
dirs, err := machine.GetMachineDirs(provider.VMType())
if err != nil {
return err
}
// TODO we could consider saying we get a list of vms but can proceed
// to just delete all local disk dirs, etc. Maybe a --proceed?
mcs, err := vmconfigs.LoadMachinesInDir(dirs)
if err != nil {
return err
}
if !resetOptions.Force {
vms := vmNamesFromMcs(mcs)
resetConfirmationMessage(vms)
reader := bufio.NewReader(os.Stdin)
fmt.Print("\nAre you sure you want to continue? [y/N] ")
answer, err := reader.ReadString('\n')
if err != nil {
return err
}
if strings.ToLower(answer)[0] != 'y' {
return nil
}
}
// resetErr can be nil or a multi-error
return shim.Reset(dirs, provider, mcs)
}
func resetConfirmationMessage(vms []string) {
fmt.Println("Warning: this command will delete all existing Podman machines")
fmt.Println("and all of the configuration and data directories for Podman machines")
fmt.Printf("\nThe following machine(s) will be deleted:\n\n")
for _, msg := range vms {
fmt.Printf("%s\n", msg)
}
}
func vmNamesFromMcs(mcs map[string]*vmconfigs.MachineConfig) []string {
keys := make([]string, 0, len(mcs))
for k := range mcs {
keys = append(keys, k)
}
return keys
}

View File

@ -0,0 +1,47 @@
% podman-machine-reset 1
## NAME
podman\-machine\-reset - Reset Podman machines and environment
## SYNOPSIS
**podman machine reset** [*options*]
## DESCRIPTION
Reset your Podman machine environment. This command stops any running machines
and then removes them. Configuration and data files are then removed. Data files
would include machine disk images and any previously pulled cache images. When
this command is run, all of your Podman machines will have been deleted.
## OPTIONS
#### **--force**, **-f**
Reset without confirmation.
#### **--help**
Print usage statement.
## EXAMPLES
```
$ podman machine reset
Warning: this command will delete all existing podman machines
and all of the configuration and data directories for Podman machines
The following machine(s) will be deleted:
dev
podman-machine-default
Are you sure you want to continue? [y/N] y
$
```
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
Feb 2024, Originally compiled by Brent Baude<bbaude@redhat.com>

View File

@ -22,21 +22,22 @@ environment variable while the machines are running can lead to unexpected behav
## SUBCOMMANDS
| Command | Man Page | Description |
|---------|-----------------------------------------------------------|--------------------------------------|
| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info |
| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine |
| inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines |
| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines |
| os | [podman-machine-os(1)](podman-machine-os.1.md) | Manage a Podman virtual machine's OS |
| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine |
| set | [podman-machine-set(1)](podman-machine-set.1.md) | Set a virtual machine setting |
| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine |
| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine |
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
| Command | Man Page | Description |
|---------|----------------------------------------------------------|---------------------------------------|
| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info |
| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine |
| inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines |
| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines |
| os | [podman-machine-os(1)](podman-machine-os.1.md) | Manage a Podman virtual machine's OS |
| reset | [podman-machine-reset(1)](podman-machine-reset.1.md) | Reset Podman machines and environment |
| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine |
| set | [podman-machine-set(1)](podman-machine-set.1.md) | Set a virtual machine setting |
| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine |
| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine |
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-os(1)](podman-machine-os.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)**
**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-os(1)](podman-machine-os.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)**, **[podman-machine-reset(1)](podman-machine-reset.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>

View File

@ -83,6 +83,10 @@ type RemoveOptions struct {
SaveIgnition bool
}
type ResetOptions struct {
Force bool
}
type InspectOptions struct{}
// TODO This can be removed when WSL is refactored into podman 5

View File

@ -0,0 +1,25 @@
package e2e_test
type resetMachine struct {
/*
-f, --force Stop and do not prompt before reseting
*/
force bool
cmd []string
}
func (i *resetMachine) buildCmd(m *machineTestBuilder) []string {
cmd := []string{"machine", "reset"}
if i.force {
cmd = append(cmd, "--force")
}
i.cmd = cmd
return cmd
}
func (i *resetMachine) withForce() *resetMachine {
i.force = true
return i
}

View File

@ -0,0 +1,88 @@
package e2e_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
var _ = Describe("podman machine reset", func() {
var (
mb *machineTestBuilder
testDir string
)
BeforeEach(func() {
testDir, mb = setup()
})
AfterEach(func() {
teardown(originalHomeDir, testDir, mb)
})
It("starting from scratch should not error", func() {
i := resetMachine{}
session, err := mb.setCmd(i.withForce()).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
})
It("reset machine with one defined machine", func() {
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
ls := new(listMachine)
beforeSession, err := mb.setCmd(ls.withNoHeading()).run()
Expect(err).ToNot(HaveOccurred())
Expect(beforeSession).To(Exit(0))
Expect(beforeSession.outputToStringSlice()).To(HaveLen(1))
reset := resetMachine{}
resetSession, err := mb.setCmd(reset.withForce()).run()
Expect(err).ToNot(HaveOccurred())
Expect(resetSession).To(Exit(0))
afterSession, err := mb.setCmd(ls.withNoHeading()).run()
Expect(err).ToNot(HaveOccurred())
Expect(afterSession).To(Exit(0))
Expect(afterSession.outputToStringSlice()).To(BeEmpty())
})
It("reset with running machine and other machines idle ", func() {
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
ls := new(listMachine)
beforeSession, err := mb.setCmd(ls.withNoHeading()).run()
Expect(err).ToNot(HaveOccurred())
Expect(beforeSession).To(Exit(0))
Expect(beforeSession.outputToStringSlice()).To(HaveLen(1))
name2 := randomString()
i2 := new(initMachine)
session2, err := mb.setName(name2).setCmd(i2.withImagePath(mb.imagePath)).run()
Expect(err).ToNot(HaveOccurred())
Expect(session2).To(Exit(0))
beforeSession, err = mb.setCmd(ls.withNoHeading()).run()
Expect(err).ToNot(HaveOccurred())
Expect(beforeSession).To(Exit(0))
Expect(beforeSession.outputToStringSlice()).To(HaveLen(2))
reset := resetMachine{}
resetSession, err := mb.setCmd(reset.withForce()).run()
Expect(err).ToNot(HaveOccurred())
Expect(resetSession).To(Exit(0))
afterSession, err := mb.setCmd(ls.withNoHeading()).run()
Expect(err).ToNot(HaveOccurred())
Expect(afterSession).To(Exit(0))
Expect(afterSession.outputToStringSlice()).To(BeEmpty())
})
})

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"time"
@ -12,6 +13,8 @@ import (
machineDefine "github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/ignition"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/containers/podman/v5/utils"
"github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus"
)
@ -446,3 +449,39 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, _ *machineDefin
return nil
}
func Reset(dirs *machineDefine.MachineDirs, mp vmconfigs.VMProvider, mcs map[string]*vmconfigs.MachineConfig) error {
var resetErrors *multierror.Error
for _, mc := range mcs {
err := Stop(mc, mp, dirs, true)
if err != nil {
resetErrors = multierror.Append(resetErrors, err)
}
_, genericRm, err := mc.Remove(false, false)
if err != nil {
resetErrors = multierror.Append(resetErrors, err)
}
_, providerRm, err := mp.Remove(mc)
if err != nil {
resetErrors = multierror.Append(resetErrors, err)
}
if err := genericRm(); err != nil {
resetErrors = multierror.Append(resetErrors, err)
}
if err := providerRm(); err != nil {
resetErrors = multierror.Append(resetErrors, err)
}
}
// Delete the various directories
// Note: we cannot delete the machine run dir blindly like this because
// other things live there like the podman.socket and so forth.
// in linux this ~/.local/share/containers/podman/machine
dataDirErr := utils.GuardedRemoveAll(filepath.Dir(dirs.DataDir.GetPath()))
// in linux this ~/.config/containers/podman/machine
confDirErr := utils.GuardedRemoveAll(filepath.Dir(dirs.ConfigDir.GetPath()))
resetErrors = multierror.Append(resetErrors, confDirErr, dataDirErr)
return resetErrors.ErrorOrNil()
}