Merge pull request #28687 from jaitjacob/add-podman-machine-restart-command

Add `podman machine restart` subcommand
This commit is contained in:
Danish Prakash 2026-07-22 09:53:10 +05:30 committed by GitHub
commit e121ed6264
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 244 additions and 18 deletions

View file

@ -0,0 +1,67 @@
//go:build amd64 || arm64
package machine
import (
"fmt"
"github.com/spf13/cobra"
"go.podman.io/podman/v6/cmd/podman/registry"
"go.podman.io/podman/v6/libpod/events"
"go.podman.io/podman/v6/pkg/machine"
"go.podman.io/podman/v6/pkg/machine/shim"
)
var (
restartCmd = &cobra.Command{
Use: "restart [options] [MACHINE]",
Short: "Restart an existing machine",
Long: "Restart a managed virtual machine",
PersistentPreRunE: machinePreRunE,
RunE: restart,
Args: cobra.MaximumNArgs(1),
Example: `podman machine restart podman-machine-default`,
ValidArgsFunction: AutocompleteMachine,
}
restartOpts = machine.StartOptions{}
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: restartCmd,
Parent: machineCmd,
})
flags := restartCmd.Flags()
noInfoFlagName := "no-info"
flags.BoolVar(&restartOpts.NoInfo, noInfoFlagName, false, "Suppress informational tips")
quietFlagName := "quiet"
flags.BoolVarP(&restartOpts.Quiet, quietFlagName, "q", false, "Suppress machine restarting status output")
}
func restart(_ *cobra.Command, args []string) error {
restartOpts.NoInfo = restartOpts.Quiet || restartOpts.NoInfo
vmName := defaultMachineName
if len(args) > 0 && len(args[0]) > 0 {
vmName = args[0]
}
mc, vmProvider, err := shim.VMExists(vmName)
if err != nil {
return err
}
if !restartOpts.Quiet {
fmt.Printf("Restarting machine %q\n", vmName)
}
updateConnection := false
if err := shim.StopThenStart(mc, vmProvider, false, restartOpts, &updateConnection); err != nil {
return err
}
fmt.Printf("Machine %q restarted successfully\n", vmName)
newMachineEvent(events.Restart, events.Event{Name: vmName})
return nil
}

View file

@ -0,0 +1,46 @@
% podman-machine-restart 1
## NAME
podman\-machine\-restart - Restart a virtual machine
## SYNOPSIS
**podman machine restart** [*options*] [*name*]
## DESCRIPTION
Restarts a virtual machine for Podman.
The default machine name is `podman-machine-default`. If a machine name is not specified as an argument,
then `podman-machine-default` will be restarted.
Stopping an already stopped virtual machine is not considered an error so running restart on a stopped
virtual machine just starts it from a stopped state.
**podman machine restart** stops and then starts a Linux virtual machine where containers are run.
## OPTIONS
#### **--help**
Print usage statement.
#### **--no-info**
Suppress informational tips.
#### **--quiet**, **-q**
Suppress machine restarting status output.
## EXAMPLES
Restart a podman machine named myvm.
```
$ podman machine restart myvm
```
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**
## HISTORY
May 2026, Originally compiled by Jait Jacob <jai8.jacob@gmail.com>

View file

@ -44,6 +44,7 @@ supported by platform. The asterisk denotes the default provider for the platfo
| 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 |
| restart | [podman-machine-restart(1)](podman-machine-restart.1.md) | Restart a virtual machine |
| 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 |
@ -51,7 +52,7 @@ supported by platform. The asterisk denotes the default provider for the platfo
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-machine-cp(1)](podman-machine-cp.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)**, **[containers.conf(5)](https://github.com/containers/container-libs/blob/main/common/docs/containers.conf.5.md)**
**[podman(1)](podman.1.md)**, **[podman-machine-cp(1)](podman-machine-cp.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)**, **[podman-machine-restart(1)](podman-machine-restart.1.md)**, **[containers.conf(5)](https://github.com/containers/container-libs/blob/main/common/docs/containers.conf.5.md)**
### Troubleshooting

View file

@ -0,0 +1,11 @@
package e2e_test
type restartMachine struct{}
func (r restartMachine) buildCmd(m *machineTestBuilder) []string {
cmd := []string{"machine", "restart"}
if len(m.name) > 0 {
cmd = append(cmd, m.name)
}
return cmd
}

View file

@ -0,0 +1,66 @@
package e2e_test
import (
"fmt"
"go.podman.io/podman/v6/pkg/machine/define"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
var _ = Describe("podman machine restart", func() {
It("should tell you when trying to restart a machine that doesn't exist", func() {
r := restartMachine{}
name := "aVMThatDoesntExist"
session, err := mb.setName(name).setCmd(r).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(125))
Expect(session.errorToString()).To(ContainSubstring("VM does not exist"))
})
It("should restart a running machine", func() {
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow().withVolume("")).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
r := restartMachine{}
restartSession, err := mb.setName(name).setCmd(r).run()
Expect(err).ToNot(HaveOccurred())
Expect(restartSession).To(Exit(0))
Expect(restartSession.outputToString()).To(ContainSubstring(fmt.Sprintf("Machine %q restarted successfully", name)))
inspect := new(inspectMachine)
inspectSession, err := mb.setName(name).setCmd(inspect.withFormat("{{.State}}")).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
Expect(inspectSession.outputToString()).To(Equal(define.Running))
})
It("should start a stopped machine", func() {
name := randomString()
i := new(initMachine)
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withVolume("")).run()
Expect(err).ToNot(HaveOccurred())
Expect(session).To(Exit(0))
inspect := new(inspectMachine)
inspectSession, err := mb.setName(name).setCmd(inspect.withFormat("{{.State}}")).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
Expect(inspectSession.outputToString()).To(Equal(define.Stopped))
r := restartMachine{}
restartSession, err := mb.setName(name).setCmd(r).run()
Expect(err).ToNot(HaveOccurred())
Expect(restartSession).To(Exit(0))
inspectSession, err = mb.setName(name).setCmd(inspect.withFormat("{{.State}}")).run()
Expect(err).ToNot(HaveOccurred())
Expect(inspectSession).To(Exit(0))
Expect(inspectSession.outputToString()).To(Equal(define.Running))
})
})

View file

@ -431,6 +431,39 @@ func Stop(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, hardStop bool) e
return stopLocked(mc, mp, dirs, hardStop)
}
// StopThenStart stops and starts the machine while holding its lock.
func StopThenStart(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, hardStop bool, opts machine.StartOptions, updateSystemConn *bool) error {
dirs, err := env.GetMachineDirs(mp.VMType())
if err != nil {
return err
}
mc.Lock()
defer mc.Unlock()
err = mc.Refresh()
if err != nil {
err = fmt.Errorf("reload config: %w", err)
return err
}
err = stopLocked(mc, mp, dirs, hardStop)
if err != nil {
return err
}
err = mc.Refresh()
if err != nil {
err = fmt.Errorf("reload config: %w", err)
return err
}
callbackFuncs := machine.CleanUp()
defer callbackFuncs.CleanIfErr(&err)
go callbackFuncs.CleanOnSignal(opts.Quiet)
err = startLocked(mc, mp, dirs, opts, updateSystemConn, &callbackFuncs)
return err
}
// stopLocked stops the machine and expects the caller to hold the machine's lock.
func stopLocked(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, hardStop bool) error {
state, err := mp.State(mc, false)
@ -476,17 +509,11 @@ func stopLocked(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *mach
}
func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machine.StartOptions, updateSystemConn *bool) error {
var (
err error
updateDefaultConnection bool
)
dirs, err := env.GetMachineDirs(mp.VMType())
if err != nil {
return err
}
defaultBackoff := 500 * time.Millisecond
maxBackoffs := 6
// startup rollaback functions
// called if an error occurs or if a
// a termination signal is sent
callbackFuncs := machine.CleanUp()
defer callbackFuncs.CleanIfErr(&err)
// The following goroutine will block waiting
@ -494,11 +521,6 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machine.St
// the routine is aborted when the main goroutine terminates.
go callbackFuncs.CleanOnSignal(opts.Quiet)
dirs, err := env.GetMachineDirs(mp.VMType())
if err != nil {
return err
}
if !opts.ReExec {
mc.Lock()
// Use sync.Once to ensure that `mc.Unlock()` is called
@ -521,10 +543,23 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, opts machine.St
defer func() { _ = mcunlock() }()
}
if err = mc.Refresh(); err != nil {
return fmt.Errorf("reload config: %w", err)
err = mc.Refresh()
if err != nil {
err = fmt.Errorf("reload config: %w", err)
return err
}
err = startLocked(mc, mp, dirs, opts, updateSystemConn, &callbackFuncs)
return err
}
// startLocked starts the machine and expects the caller to hold the machine's lock.
func startLocked(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, opts machine.StartOptions, updateSystemConn *bool, callbackFuncs *machine.CleanupCallback) error {
var updateDefaultConnection bool
defaultBackoff := 500 * time.Millisecond
maxBackoffs := 6
connName := mc.Name
if mc.HostUser.Rootful {
connName += "-root"