github.com/containers/podman/v4@v4.9.4/pkg/machine/applehv/rest.go (about) 1 //go:build darwin 2 // +build darwin 3 4 package applehv 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "net/http" 13 "time" 14 15 "github.com/containers/podman/v4/pkg/machine" 16 "github.com/crc-org/vfkit/pkg/rest/define" 17 "github.com/sirupsen/logrus" 18 "golang.org/x/sys/unix" 19 ) 20 21 type Endpoint string 22 23 const ( 24 inspect = "/vm/inspect" 25 state = "/vm/state" 26 version = "/version" 27 ) 28 29 func (vf *VfkitHelper) get(endpoint string, payload io.Reader) (*http.Response, error) { 30 client := &http.Client{} 31 req, err := http.NewRequest(http.MethodGet, endpoint, payload) 32 if err != nil { 33 return nil, err 34 } 35 return client.Do(req) 36 } 37 38 func (vf *VfkitHelper) post(endpoint string, payload io.Reader) (*http.Response, error) { 39 client := &http.Client{} 40 req, err := http.NewRequest(http.MethodPost, endpoint, payload) 41 if err != nil { 42 return nil, err 43 } 44 return client.Do(req) 45 } 46 47 // getRawState asks vfkit for virtual machine state unmodified (see state()) 48 func (vf *VfkitHelper) getRawState() (machine.Status, error) { 49 var response define.VMState 50 endPoint := vf.Endpoint + state 51 serverResponse, err := vf.get(endPoint, nil) 52 if err != nil { 53 if errors.Is(err, unix.ECONNREFUSED) { 54 logrus.Debugf("connection refused: %s", endPoint) 55 } 56 return "", err 57 } 58 err = json.NewDecoder(serverResponse.Body).Decode(&response) 59 if err != nil { 60 return "", err 61 } 62 return ToMachineStatus(response.State) 63 64 } 65 66 // state asks vfkit for the virtual machine state. in case the vfkit 67 // service is not responding, we assume the service is not running 68 // and return a stopped status 69 func (vf *VfkitHelper) state() (machine.Status, error) { 70 vmState, err := vf.getRawState() 71 if err == nil { 72 return vmState, err 73 } 74 if errors.Is(err, unix.ECONNREFUSED) { 75 return machine.Stopped, nil 76 } 77 return "", err 78 } 79 80 func (vf *VfkitHelper) stateChange(newState define.StateChange) error { 81 b, err := json.Marshal(define.VMState{State: string(newState)}) 82 if err != nil { 83 return err 84 } 85 payload := bytes.NewReader(b) 86 _, err = vf.post(vf.Endpoint+state, payload) 87 return err 88 } 89 90 func (vf *VfkitHelper) stop(force, wait bool) error { 91 waitDuration := time.Millisecond * 10 92 // TODO Add ability to wait until stopped 93 if force { 94 if err := vf.stateChange(define.HardStop); err != nil { 95 return err 96 } 97 } else { 98 if err := vf.stateChange(define.Stop); err != nil { 99 return err 100 } 101 } 102 if !wait { 103 return nil 104 } 105 waitErr := fmt.Errorf("failed waiting for vm to stop") 106 // Backoff to wait on the machine shutdown 107 for i := 0; i < 11; i++ { 108 _, err := vf.getRawState() 109 if err != nil || errors.Is(err, unix.ECONNREFUSED) { 110 waitErr = nil 111 break 112 } 113 waitDuration = waitDuration * 2 114 logrus.Debugf("backoff wait time: %s", waitDuration.String()) 115 time.Sleep(waitDuration) 116 } 117 return waitErr 118 }