mirror of
https://github.com/containers/podman.git
synced 2025-11-03 15:56:51 +08:00
This PR adds libkrun support to podman machine. This is an experimental feature and should not be marketed yet. Before we unmark the experimental status on this function, we will need to have full CI support and a full podman point release has pased. This work relies on the fact that vfkit and libkrun share a reasonably (if not perfectly) same API. The --log-level debug option will not show a GUI screen for boots as krun is not capable of this. Signed-off-by: Brent Baude <bbaude@redhat.com>
128 lines
3.0 KiB
Go
128 lines
3.0 KiB
Go
//go:build darwin
|
|
|
|
package vfkit
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"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 {
|
|
waitDuration := time.Millisecond * 10
|
|
// TODO Add ability to wait until stopped
|
|
if force {
|
|
if err := vf.stateChange(rest.HardStop); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := vf.stateChange(rest.Stop); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !wait {
|
|
return nil
|
|
}
|
|
waitErr := fmt.Errorf("failed waiting for vm to stop")
|
|
// Backoff to wait on the machine shutdown
|
|
for i := 0; i < 11; i++ {
|
|
_, err := vf.getRawState()
|
|
if err != nil || errors.Is(err, unix.ECONNREFUSED) {
|
|
waitErr = nil
|
|
break
|
|
}
|
|
waitDuration *= 2
|
|
logrus.Debugf("backoff wait time: %s", waitDuration.String())
|
|
time.Sleep(waitDuration)
|
|
}
|
|
return waitErr
|
|
}
|
|
|
|
// Helper describes the use of vfkit: cmdline and endpoint
|
|
type Helper struct {
|
|
LogLevel logrus.Level
|
|
Endpoint string
|
|
BinaryPath *define.VMFile
|
|
VirtualMachine *config.VirtualMachine
|
|
}
|