mirror of
https://github.com/containers/podman.git
synced 2025-11-02 14:55:28 +08:00
The current timeout was not long enough. Systemd default is 90s so we should wait for at least that long. Also it really doesn't make sense to throw an error we saying we failed waiting for stop. We should hard terminate the VM in case a graceful shutdown did not happen. Fixes #22515 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
123 lines
2.9 KiB
Go
123 lines
2.9 KiB
Go
//go:build darwin
|
|
|
|
package vfkit
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/containers/podman/v5/pkg/machine/define"
|
|
"github.com/crc-org/vfkit/pkg/config"
|
|
rest "github.com/crc-org/vfkit/pkg/rest/define"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const (
|
|
inspect = "/vm/inspect"
|
|
state = "/vm/state"
|
|
version = "/version"
|
|
)
|
|
|
|
func (vf *Helper) get(endpoint string, payload io.Reader) (*http.Response, error) {
|
|
client := &http.Client{}
|
|
req, err := http.NewRequest(http.MethodGet, endpoint, payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return client.Do(req)
|
|
}
|
|
|
|
func (vf *Helper) post(endpoint string, payload io.Reader) (*http.Response, error) {
|
|
client := &http.Client{}
|
|
req, err := http.NewRequest(http.MethodPost, endpoint, payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return client.Do(req)
|
|
}
|
|
|
|
// getRawState asks vfkit for virtual machine state unmodified (see state())
|
|
func (vf *Helper) getRawState() (define.Status, error) {
|
|
var response rest.VMState
|
|
endPoint := vf.Endpoint + state
|
|
serverResponse, err := vf.get(endPoint, nil)
|
|
if err != nil {
|
|
if errors.Is(err, unix.ECONNREFUSED) {
|
|
logrus.Debugf("connection refused: %s", endPoint)
|
|
}
|
|
return "", err
|
|
}
|
|
err = json.NewDecoder(serverResponse.Body).Decode(&response)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err := serverResponse.Body.Close(); err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
return ToMachineStatus(response.State)
|
|
}
|
|
|
|
// state asks vfkit for the virtual machine state. in case the vfkit
|
|
// service is not responding, we assume the service is not running
|
|
// and return a stopped status
|
|
func (vf *Helper) State() (define.Status, error) {
|
|
vmState, err := vf.getRawState()
|
|
if err == nil {
|
|
return vmState, nil
|
|
}
|
|
if errors.Is(err, unix.ECONNREFUSED) {
|
|
return define.Stopped, nil
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
func (vf *Helper) stateChange(newState rest.StateChange) error {
|
|
b, err := json.Marshal(rest.VMState{State: string(newState)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
payload := bytes.NewReader(b)
|
|
serverResponse, err := vf.post(vf.Endpoint+state, payload)
|
|
_ = serverResponse.Body.Close()
|
|
return err
|
|
}
|
|
|
|
func (vf *Helper) Stop(force, wait bool) error {
|
|
state := rest.Stop
|
|
if force {
|
|
state = rest.HardStop
|
|
}
|
|
if err := vf.stateChange(state); err != nil {
|
|
return err
|
|
}
|
|
if !wait {
|
|
return nil
|
|
}
|
|
waitDuration := time.Millisecond * 500
|
|
// Wait up to 90s then hard force off
|
|
for i := 0; i < 180; i++ {
|
|
_, err := vf.getRawState()
|
|
if err != nil || errors.Is(err, unix.ECONNREFUSED) {
|
|
return nil
|
|
}
|
|
time.Sleep(waitDuration)
|
|
}
|
|
logrus.Warn("Failed to gracefully stop machine, performing hard stop")
|
|
// we waited long enough do a hard stop
|
|
return vf.stateChange(rest.HardStop)
|
|
}
|
|
|
|
// Helper describes the use of vfkit: cmdline and endpoint
|
|
type Helper struct {
|
|
LogLevel logrus.Level
|
|
Endpoint string
|
|
BinaryPath *define.VMFile
|
|
VirtualMachine *config.VirtualMachine
|
|
Rosetta bool
|
|
}
|