mirror of https://github.com/containers/podman.git
126 lines
2.9 KiB
Go
126 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)
|
|
if err == nil {
|
|
_ = 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 {
|
|
//nolint:nilerr // error means vfkit is gone so machine is stopped
|
|
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
|
|
}
|