diff options
Diffstat (limited to 'pkg/machine')
-rw-r--r-- | pkg/machine/config_test.go | 3 | ||||
-rw-r--r-- | pkg/machine/keys.go | 21 | ||||
-rw-r--r-- | pkg/machine/qemu/config.go | 4 | ||||
-rw-r--r-- | pkg/machine/qemu/config_test.go | 3 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 107 | ||||
-rw-r--r-- | pkg/machine/qemu/machine_test.go | 3 |
6 files changed, 111 insertions, 30 deletions
diff --git a/pkg/machine/config_test.go b/pkg/machine/config_test.go index d9fc5425e..ca08660b9 100644 --- a/pkg/machine/config_test.go +++ b/pkg/machine/config_test.go @@ -1,3 +1,6 @@ +//go:build amd64 || arm64 +// +build amd64 arm64 + package machine import ( diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go index 45d9801cc..463271427 100644 --- a/pkg/machine/keys.go +++ b/pkg/machine/keys.go @@ -4,14 +4,15 @@ package machine import ( - "errors" "fmt" + "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -51,7 +52,23 @@ func CreateSSHKeysPrefix(dir string, file string, passThru bool, skipExisting bo // generatekeys creates an ed25519 set of keys func generatekeys(writeLocation string) error { args := append(append([]string{}, sshCommand[1:]...), writeLocation) - return exec.Command(sshCommand[0], args...).Run() + cmd := exec.Command(sshCommand[0], args...) + stdErr, err := cmd.StderrPipe() + if err != nil { + return err + } + if err := cmd.Start(); err != nil { + return err + } + waitErr := cmd.Wait() + if waitErr == nil { + return nil + } + errMsg, err := io.ReadAll(stdErr) + if err != nil { + return fmt.Errorf("key generation failed, unable to read from stderr: %w", waitErr) + } + return fmt.Errorf("failed to generate keys: %s: %w", string(errMsg), waitErr) } // generatekeys creates an ed25519 set of keys diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 56c95e3b3..bada1af9b 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -72,8 +72,10 @@ type MachineVM struct { Mounts []machine.Mount // Name of VM Name string - // PidFilePath is the where the PID file lives + // PidFilePath is the where the Proxy PID file lives 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 Monitor // ReadySocket tells host when vm is booted diff --git a/pkg/machine/qemu/config_test.go b/pkg/machine/qemu/config_test.go index 4d96ec6e7..72cb3ed90 100644 --- a/pkg/machine/qemu/config_test.go +++ b/pkg/machine/qemu/config_test.go @@ -1,3 +1,6 @@ +//go:build (amd64 && !windows) || (arm64 && !windows) +// +build amd64,!windows arm64,!windows + package qemu import ( diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 3a1495021..7e9c786a9 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -32,6 +32,7 @@ import ( "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) var ( @@ -107,6 +108,9 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { if err != nil { return nil, err } + if err := vm.setPIDSocket(); err != nil { + return nil, err + } cmd := []string{execPath} // Add 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", // 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", - "-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 - if err := vm.setPIDSocket(); err != nil { - return nil, err - } 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) { return nil } - pidString, err := v.PidFilePath.Read() + proxyPidString, err := v.PidFilePath.Read() if err != nil { return err } - pidNum, err := strconv.Atoi(string(pidString)) + proxyPid, err := strconv.Atoi(string(proxyPidString)) if err != nil { return err } - p, err := os.FindProcess(pidNum) - if p == nil && err != nil { + proxyProc, err := os.FindProcess(proxyPid) + if proxyProc == nil && err != nil { return err } @@ -772,7 +774,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error { return err } // Kill the process - if err := p.Kill(); err != nil { + if err := proxyProc.Kill(); err != nil { return err } // Remove the pidfile @@ -788,22 +790,50 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error { // FIXME: this error should probably be returned return nil //nolint: nilerr } - disconnected = true - 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 + + if err := v.ReadySocket.Delete(); err != nil { + return err + } + + 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 } - 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 } - return v.ReadySocket.Delete() + 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 @@ -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 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 { - logrus.Debugf("Error while removing pidfile: %v", err) + logrus.Debugf("Error while removing proxy pidfile: %v", err) } // Remove socket if err := v.QMPMonitor.Address.Delete(); err != nil { @@ -930,7 +963,12 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) { } monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout) if err != nil { - // FIXME: this error should probably be returned + // If an improper cleanup was done and the socketmonitor was not deleted, + // it can appear as though the machine state is not stopped. Check for ECONNREFUSED + // almost assures us that the vm is stopped. + if errors.Is(err, syscall.ECONNREFUSED) { + return machine.Stopped, nil + } return "", err } if err := monitor.Connect(); err != nil { @@ -975,7 +1013,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error { port := strconv.Itoa(v.Port) args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile=/dev/null", - "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR"} + "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="} if len(opts.Args) > 0 { args = append(args, opts.Args...) } else { @@ -1305,13 +1343,19 @@ func (v *MachineVM) setPIDSocket() error { if !rootless.IsRootless() { rtPath = "/run" } - pidFileName := fmt.Sprintf("%s.pid", v.Name) 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 { + return err + } + proxyPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, proxyPidFileName), &proxyPidFileName) if err != nil { return err } - v.PidFilePath = *pidFilePath + v.VMPidFilePath = *vmPidFilePath + v.PidFilePath = *proxyPidFilePath return nil } @@ -1648,3 +1692,12 @@ func (p *Provider) RemoveAndCleanMachines() error { } return prevErr } + +func isProcessAlive(pid int) bool { + err := unix.Kill(pid, syscall.Signal(0)) + if err == nil || err == unix.EPERM { + return true + } + + return false +} diff --git a/pkg/machine/qemu/machine_test.go b/pkg/machine/qemu/machine_test.go index 62ca6068a..4c393d0f4 100644 --- a/pkg/machine/qemu/machine_test.go +++ b/pkg/machine/qemu/machine_test.go @@ -1,3 +1,6 @@ +//go:build (amd64 && !windows) || (arm64 && !windows) +// +build amd64,!windows arm64,!windows + package qemu import ( |