mirror of
https://github.com/containers/podman.git
synced 2025-06-25 12:20:42 +08:00
Merge pull request #14666 from shanesmith/machine-pidfile
Make `podman machine stop` wait for qemu to exit
This commit is contained in:
@ -72,8 +72,10 @@ type MachineVM struct {
|
|||||||
Mounts []machine.Mount
|
Mounts []machine.Mount
|
||||||
// Name of VM
|
// Name of VM
|
||||||
Name string
|
Name string
|
||||||
// PidFilePath is the where the PID file lives
|
// PidFilePath is the where the Proxy PID file lives
|
||||||
PidFilePath machine.VMFile
|
PidFilePath machine.VMFile
|
||||||
|
// VMPidFilePath is the where the VM PID file lives
|
||||||
|
VMPidFilePath machine.VMFile
|
||||||
// QMPMonitor is the qemu monitor object for sending commands
|
// QMPMonitor is the qemu monitor object for sending commands
|
||||||
QMPMonitor Monitor
|
QMPMonitor Monitor
|
||||||
// ReadySocket tells host when vm is booted
|
// ReadySocket tells host when vm is booted
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -107,6 +108,9 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := vm.setPIDSocket(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
cmd := []string{execPath}
|
cmd := []string{execPath}
|
||||||
// Add memory
|
// Add memory
|
||||||
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
|
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
|
||||||
@ -135,11 +139,9 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
|||||||
"-device", "virtio-serial",
|
"-device", "virtio-serial",
|
||||||
// qemu needs to establish the long name; other connections can use the symlink'd
|
// qemu needs to establish the long name; other connections can use the symlink'd
|
||||||
"-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=" + vm.Name + "_ready",
|
"-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=" + vm.Name + "_ready",
|
||||||
"-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...)
|
"-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0",
|
||||||
|
"-pidfile", vm.VMPidFilePath.GetPath()}...)
|
||||||
vm.CmdLine = cmd
|
vm.CmdLine = cmd
|
||||||
if err := vm.setPIDSocket(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return vm, nil
|
return vm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,17 +755,17 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
|
|||||||
if _, err := os.Stat(v.PidFilePath.GetPath()); os.IsNotExist(err) {
|
if _, err := os.Stat(v.PidFilePath.GetPath()); os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pidString, err := v.PidFilePath.Read()
|
proxyPidString, err := v.PidFilePath.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pidNum, err := strconv.Atoi(string(pidString))
|
proxyPid, err := strconv.Atoi(string(proxyPidString))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := os.FindProcess(pidNum)
|
proxyProc, err := os.FindProcess(proxyPid)
|
||||||
if p == nil && err != nil {
|
if proxyProc == nil && err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,7 +774,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Kill the process
|
// Kill the process
|
||||||
if err := p.Kill(); err != nil {
|
if err := proxyProc.Kill(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Remove the pidfile
|
// Remove the pidfile
|
||||||
@ -788,22 +790,50 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
|
|||||||
// FIXME: this error should probably be returned
|
// FIXME: this error should probably be returned
|
||||||
return nil //nolint: nilerr
|
return nil //nolint: nilerr
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnected = true
|
disconnected = true
|
||||||
waitInternal := 250 * time.Millisecond
|
|
||||||
for i := 0; i < 5; i++ {
|
if err := v.ReadySocket.Delete(); err != nil {
|
||||||
state, err := v.State(false)
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if state != machine.Running {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(waitInternal)
|
|
||||||
waitInternal *= 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.ReadySocket.Delete()
|
if v.VMPidFilePath.GetPath() == "" {
|
||||||
|
// no vm pid file path means it's probably a machine created before we
|
||||||
|
// started using it, so we revert to the old way of waiting for the
|
||||||
|
// machine to stop
|
||||||
|
fmt.Println("Waiting for VM to stop running...")
|
||||||
|
waitInternal := 250 * time.Millisecond
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
state, err := v.State(false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if state != machine.Running {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(waitInternal)
|
||||||
|
waitInternal *= 2
|
||||||
|
}
|
||||||
|
// after the machine stops running it normally takes about 1 second for the
|
||||||
|
// qemu VM to exit so we wait a bit to try to avoid issues
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vmPidString, err := v.VMPidFilePath.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vmPid, err := strconv.Atoi(strings.TrimSpace(string(vmPidString)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Waiting for VM to exit...")
|
||||||
|
for isProcessAlive(vmPid) {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQMPMonitor creates the monitor subsection of our vm
|
// NewQMPMonitor creates the monitor subsection of our vm
|
||||||
@ -896,8 +926,11 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
|
|||||||
|
|
||||||
// remove socket and pid file if any: warn at low priority if things fail
|
// remove socket and pid file if any: warn at low priority if things fail
|
||||||
// Remove the pidfile
|
// Remove the pidfile
|
||||||
|
if err := v.VMPidFilePath.Delete(); err != nil {
|
||||||
|
logrus.Debugf("Error while removing VM pidfile: %v", err)
|
||||||
|
}
|
||||||
if err := v.PidFilePath.Delete(); err != nil {
|
if err := v.PidFilePath.Delete(); err != nil {
|
||||||
logrus.Debugf("Error while removing pidfile: %v", err)
|
logrus.Debugf("Error while removing proxy pidfile: %v", err)
|
||||||
}
|
}
|
||||||
// Remove socket
|
// Remove socket
|
||||||
if err := v.QMPMonitor.Address.Delete(); err != nil {
|
if err := v.QMPMonitor.Address.Delete(); err != nil {
|
||||||
@ -1305,13 +1338,19 @@ func (v *MachineVM) setPIDSocket() error {
|
|||||||
if !rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
rtPath = "/run"
|
rtPath = "/run"
|
||||||
}
|
}
|
||||||
pidFileName := fmt.Sprintf("%s.pid", v.Name)
|
|
||||||
socketDir := filepath.Join(rtPath, "podman")
|
socketDir := filepath.Join(rtPath, "podman")
|
||||||
pidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, pidFileName), &pidFileName)
|
vmPidFileName := fmt.Sprintf("%s_vm.pid", v.Name)
|
||||||
|
proxyPidFileName := fmt.Sprintf("%s_proxy.pid", v.Name)
|
||||||
|
vmPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, vmPidFileName), &vmPidFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.PidFilePath = *pidFilePath
|
proxyPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, proxyPidFileName), &proxyPidFileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.VMPidFilePath = *vmPidFilePath
|
||||||
|
v.PidFilePath = *proxyPidFilePath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1648,3 +1687,12 @@ func (p *Provider) RemoveAndCleanMachines() error {
|
|||||||
}
|
}
|
||||||
return prevErr
|
return prevErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isProcessAlive(pid int) bool {
|
||||||
|
err := unix.Kill(pid, syscall.Signal(0))
|
||||||
|
if err == nil || err == unix.EPERM {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user