mirror of
https://github.com/containers/podman.git
synced 2026-03-23 00:51:37 -04:00
Merge pull request #20988 from baude/p5standardmachineconfig
Podman 5 machine config file - Step 1
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/pkg/errorhandling"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/provider"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -184,7 +185,7 @@ func composeDockerHost() (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("inspecting machine: %w", err)
|
||||
}
|
||||
if info.State != machine.Running {
|
||||
if info.State != define.Running {
|
||||
return "", fmt.Errorf("machine %s is not running but in state %s", item.Name, info.State)
|
||||
}
|
||||
if machineProvider.VMType() == machine.WSLVirt {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/compression"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
vfConfig "github.com/crc-org/vfkit/pkg/config"
|
||||
"github.com/docker/go-units"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -76,10 +77,10 @@ func (v AppleHVVirtualization) List(opts machine.ListOptions) ([]*machine.ListRe
|
||||
}
|
||||
|
||||
for _, mm := range mms {
|
||||
vmState, err := mm.Vfkit.state()
|
||||
vmState, err := mm.Vfkit.State()
|
||||
if err != nil {
|
||||
if errors.Is(err, unix.ECONNREFUSED) {
|
||||
vmState = machine.Stopped
|
||||
vmState = define.Stopped
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,8 +90,8 @@ func (v AppleHVVirtualization) List(opts machine.ListOptions) ([]*machine.ListRe
|
||||
Name: mm.Name,
|
||||
CreatedAt: mm.Created,
|
||||
LastUp: mm.LastUp,
|
||||
Running: vmState == machine.Running,
|
||||
Starting: vmState == machine.Starting,
|
||||
Running: vmState == define.Running,
|
||||
Starting: vmState == define.Starting,
|
||||
Stream: mm.ImageStream,
|
||||
VMType: machine.AppleHvVirt.String(),
|
||||
CPUs: mm.CPUs,
|
||||
@@ -140,7 +141,7 @@ func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
|
||||
// Set creation time
|
||||
m.Created = time.Now()
|
||||
|
||||
m.ResourceConfig = machine.ResourceConfig{
|
||||
m.ResourceConfig = vmconfigs.ResourceConfig{
|
||||
CPUs: opts.CPUS,
|
||||
DiskSize: opts.DiskSize,
|
||||
// Diskpath will be needed
|
||||
|
||||
@@ -21,7 +21,10 @@ import (
|
||||
"github.com/containers/common/pkg/config"
|
||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/applehv/vfkit"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/sockets"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v4/pkg/strongunits"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
@@ -43,14 +46,6 @@ const (
|
||||
apiUpTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
// VfkitHelper describes the use of vfkit: cmdline and endpoint
|
||||
type VfkitHelper struct {
|
||||
LogLevel logrus.Level
|
||||
Endpoint string
|
||||
VfkitBinaryPath *define.VMFile
|
||||
VirtualMachine *vfConfig.VirtualMachine
|
||||
}
|
||||
|
||||
// appleHVReadyUnit is a unit file that sets up the virtual serial device
|
||||
// where when the VM is done configuring, it will send an ack
|
||||
// so a listening host knows it can begin interacting with it
|
||||
@@ -71,19 +66,19 @@ type MacMachine struct {
|
||||
// ConfigPath is the fully qualified path to the configuration file
|
||||
ConfigPath define.VMFile
|
||||
// HostUser contains info about host user
|
||||
machine.HostUser
|
||||
vmconfigs.HostUser
|
||||
// ImageConfig describes the bootable image
|
||||
machine.ImageConfig
|
||||
// Mounts is the list of remote filesystems to mount
|
||||
Mounts []machine.Mount
|
||||
Mounts []vmconfigs.Mount
|
||||
// Name of VM
|
||||
Name string
|
||||
// ReadySocket tells host when vm is booted
|
||||
ReadySocket define.VMFile
|
||||
// ResourceConfig is physical attrs of the VM
|
||||
machine.ResourceConfig
|
||||
vmconfigs.ResourceConfig
|
||||
// SSHConfig for accessing the remote vm
|
||||
machine.SSHConfig
|
||||
vmconfigs.SSHConfig
|
||||
// Starting tells us whether the machine is running or if we have just dialed it to start it
|
||||
Starting bool
|
||||
// Created contains the original created time instead of querying the file mod time
|
||||
@@ -91,7 +86,7 @@ type MacMachine struct {
|
||||
// LastUp contains the last recorded uptime
|
||||
LastUp time.Time
|
||||
// The VFKit endpoint where we can interact with the VM
|
||||
Vfkit VfkitHelper
|
||||
Vfkit vfkit.VfkitHelper
|
||||
LogPath define.VMFile
|
||||
GvProxyPid define.VMFile
|
||||
GvProxySock define.VMFile
|
||||
@@ -108,7 +103,7 @@ func (m *MacMachine) setGVProxyInfo(runtimeDir string) error {
|
||||
}
|
||||
m.GvProxyPid = *gvProxyPid
|
||||
|
||||
return machine.SetSocket(&m.GvProxySock, filepath.Join(runtimeDir, "gvproxy.sock"), nil)
|
||||
return sockets.SetSocket(&m.GvProxySock, filepath.Join(runtimeDir, "gvproxy.sock"), nil)
|
||||
}
|
||||
|
||||
// setVfkitInfo stores the default devices, sets the vfkit endpoint, and
|
||||
@@ -138,7 +133,7 @@ func (m *MacMachine) setVfkitInfo(cfg *config.Config, readySocket define.VMFile)
|
||||
// addMountsToVM converts the volumes passed through the CLI to virtio-fs mounts
|
||||
// and adds them to the machine
|
||||
func (m *MacMachine) addMountsToVM(opts machine.InitOptions, virtiofsMnts *[]machine.VirtIoFs) error {
|
||||
var mounts []machine.Mount
|
||||
var mounts []vmconfigs.Mount
|
||||
for _, volume := range opts.Volumes {
|
||||
source, target, _, readOnly, err := machine.ParseVolumeFromPath(volume)
|
||||
if err != nil {
|
||||
@@ -202,7 +197,7 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := machine.SetSocket(&m.ReadySocket, machine.ReadySocketPath(runtimeDir, m.Name), nil); err != nil {
|
||||
if err := sockets.SetSocket(&m.ReadySocket, sockets.ReadySocketPath(runtimeDir, m.Name), nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -305,7 +300,7 @@ func (m *MacMachine) removeSystemConnections() error {
|
||||
}
|
||||
|
||||
func (m *MacMachine) Inspect() (*machine.InspectInfo, error) {
|
||||
vmState, err := m.Vfkit.state()
|
||||
vmState, err := m.Vfkit.State()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -329,7 +324,7 @@ func (m *MacMachine) Inspect() (*machine.InspectInfo, error) {
|
||||
},
|
||||
LastUp: m.LastUp,
|
||||
Name: m.Name,
|
||||
Resources: machine.ResourceConfig{
|
||||
Resources: vmconfigs.ResourceConfig{
|
||||
CPUs: m.CPUs,
|
||||
DiskSize: m.DiskSize,
|
||||
Memory: m.Memory,
|
||||
@@ -367,16 +362,16 @@ func (m *MacMachine) Remove(name string, opts machine.RemoveOptions) (string, fu
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
vmState, err := m.Vfkit.state()
|
||||
vmState, err := m.Vfkit.State()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if vmState == machine.Running {
|
||||
if vmState == define.Running {
|
||||
if !opts.Force {
|
||||
return "", nil, &machine.ErrVMRunningCannotDestroyed{Name: m.Name}
|
||||
}
|
||||
if err := m.Vfkit.stop(true, true); err != nil {
|
||||
if err := m.Vfkit.Stop(true, true); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer func() {
|
||||
@@ -430,7 +425,7 @@ func (m *MacMachine) Set(name string, opts machine.SetOptions) ([]error, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vmState != machine.Stopped {
|
||||
if vmState != define.Stopped {
|
||||
return nil, machine.ErrWrongState
|
||||
}
|
||||
if cpus := opts.CPUs; cpus != nil {
|
||||
@@ -473,7 +468,7 @@ func (m *MacMachine) SSH(name string, opts machine.SSHOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if st != machine.Running {
|
||||
if st != define.Running {
|
||||
return fmt.Errorf("vm %q is not running", m.Name)
|
||||
}
|
||||
username := opts.Username
|
||||
@@ -561,7 +556,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if st == machine.Running {
|
||||
if st == define.Running {
|
||||
return machine.ErrVMAlreadyRunning
|
||||
}
|
||||
|
||||
@@ -664,7 +659,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
|
||||
logrus.Debug("waiting for ready notification")
|
||||
readyChan := make(chan error)
|
||||
go machine.ListenAndWaitOnSocket(readyChan, readyListen)
|
||||
go sockets.ListenAndWaitOnSocket(readyChan, readyListen)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
@@ -715,8 +710,8 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MacMachine) State(_ bool) (machine.Status, error) {
|
||||
vmStatus, err := m.Vfkit.state()
|
||||
func (m *MacMachine) State(_ bool) (define.Status, error) {
|
||||
vmStatus, err := m.Vfkit.State()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -732,7 +727,7 @@ func (m *MacMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if vmState != machine.Running {
|
||||
if vmState != define.Running {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -742,7 +737,7 @@ func (m *MacMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
}
|
||||
}()
|
||||
|
||||
return m.Vfkit.stop(false, true)
|
||||
return m.Vfkit.Stop(false, true)
|
||||
}
|
||||
|
||||
// getVMConfigPath is a simple wrapper for getting the fully-qualified
|
||||
@@ -845,7 +840,7 @@ func getVMInfos() ([]*machine.ListResponse, error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listEntry.Running = vmState == machine.Running
|
||||
listEntry.Running = vmState == define.Running
|
||||
listEntry.LastUp = vm.LastUp
|
||||
|
||||
listed = append(listed, listEntry)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package applehv
|
||||
package vfkit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -12,14 +12,13 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/crc-org/vfkit/pkg/rest/define"
|
||||
"github.com/containers/podman/v4/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"
|
||||
)
|
||||
|
||||
type Endpoint string
|
||||
|
||||
const (
|
||||
inspect = "/vm/inspect"
|
||||
state = "/vm/state"
|
||||
@@ -45,8 +44,8 @@ func (vf *VfkitHelper) post(endpoint string, payload io.Reader) (*http.Response,
|
||||
}
|
||||
|
||||
// getRawState asks vfkit for virtual machine state unmodified (see state())
|
||||
func (vf *VfkitHelper) getRawState() (machine.Status, error) {
|
||||
var response define.VMState
|
||||
func (vf *VfkitHelper) getRawState() (define.Status, error) {
|
||||
var response rest.VMState
|
||||
endPoint := vf.Endpoint + state
|
||||
serverResponse, err := vf.get(endPoint, nil)
|
||||
if err != nil {
|
||||
@@ -60,25 +59,24 @@ func (vf *VfkitHelper) getRawState() (machine.Status, error) {
|
||||
return "", 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 *VfkitHelper) state() (machine.Status, error) {
|
||||
func (vf *VfkitHelper) State() (define.Status, error) {
|
||||
vmState, err := vf.getRawState()
|
||||
if err == nil {
|
||||
return vmState, err
|
||||
}
|
||||
if errors.Is(err, unix.ECONNREFUSED) {
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (vf *VfkitHelper) stateChange(newState define.StateChange) error {
|
||||
b, err := json.Marshal(define.VMState{State: string(newState)})
|
||||
func (vf *VfkitHelper) stateChange(newState rest.StateChange) error {
|
||||
b, err := json.Marshal(rest.VMState{State: string(newState)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -87,15 +85,15 @@ func (vf *VfkitHelper) stateChange(newState define.StateChange) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (vf *VfkitHelper) stop(force, wait bool) error {
|
||||
func (vf *VfkitHelper) Stop(force, wait bool) error {
|
||||
waitDuration := time.Millisecond * 10
|
||||
// TODO Add ability to wait until stopped
|
||||
if force {
|
||||
if err := vf.stateChange(define.HardStop); err != nil {
|
||||
if err := vf.stateChange(rest.HardStop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := vf.stateChange(define.Stop); err != nil {
|
||||
if err := vf.stateChange(rest.Stop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -116,3 +114,11 @@ func (vf *VfkitHelper) stop(force, wait bool) error {
|
||||
}
|
||||
return waitErr
|
||||
}
|
||||
|
||||
// VfkitHelper describes the use of vfkit: cmdline and endpoint
|
||||
type VfkitHelper struct {
|
||||
LogLevel logrus.Level
|
||||
Endpoint string
|
||||
VfkitBinaryPath *define.VMFile
|
||||
VirtualMachine *config.VirtualMachine
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package applehv
|
||||
package vfkit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
)
|
||||
|
||||
type Endpoint string
|
||||
|
||||
// VZMachineState is what the restful service in vfkit will return
|
||||
type VZMachineState string
|
||||
|
||||
@@ -26,14 +28,14 @@ const (
|
||||
VZMachineStateStopping VZMachineState = "VirtualMachineStateStopping"
|
||||
)
|
||||
|
||||
func ToMachineStatus(val string) (machine.Status, error) {
|
||||
func ToMachineStatus(val string) (define.Status, error) {
|
||||
switch val {
|
||||
case string(VZMachineStateRunning), string(VZMachineStatePausing), string(VZMachineStateResuming), string(VZMachineStateStopping), string(VZMachineStatePaused):
|
||||
return machine.Running, nil
|
||||
return define.Running, nil
|
||||
case string(VZMachineStateStopped):
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
case string(VZMachineStateStarting):
|
||||
return machine.Starting, nil
|
||||
return define.Starting, nil
|
||||
case string(VZMachineStateError):
|
||||
return "", errors.New("machine is in error state")
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/containers/common/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/compression"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -43,17 +44,7 @@ type InitOptions struct {
|
||||
USBs []string
|
||||
}
|
||||
|
||||
type Status = string
|
||||
|
||||
const (
|
||||
// Running indicates the qemu vm is running.
|
||||
Running Status = "running"
|
||||
// Stopped indicates the vm has stopped.
|
||||
Stopped Status = "stopped"
|
||||
// Starting indicated the vm is in the process of starting
|
||||
Starting Status = "starting"
|
||||
// Unknown means the state is not known
|
||||
Unknown Status = "unknown"
|
||||
DefaultMachineName string = "podman-machine-default"
|
||||
apiUpTimeout = 20 * time.Second
|
||||
)
|
||||
@@ -139,7 +130,7 @@ type VM interface {
|
||||
Set(name string, opts SetOptions) ([]error, error)
|
||||
SSH(name string, opts SSHOptions) error
|
||||
Start(name string, opts StartOptions) error
|
||||
State(bypass bool) (Status, error)
|
||||
State(bypass bool) (define.Status, error)
|
||||
Stop(name string, opts StopOptions) error
|
||||
}
|
||||
|
||||
@@ -173,9 +164,9 @@ type InspectInfo struct {
|
||||
Image ImageConfig
|
||||
LastUp time.Time
|
||||
Name string
|
||||
Resources ResourceConfig
|
||||
SSHConfig SSHConfig
|
||||
State Status
|
||||
Resources vmconfigs.ResourceConfig
|
||||
SSHConfig vmconfigs.SSHConfig
|
||||
State define.Status
|
||||
UserModeNetworking bool
|
||||
Rootful bool
|
||||
}
|
||||
@@ -274,33 +265,6 @@ func ConfDirPrefix() (string, error) {
|
||||
return confDir, nil
|
||||
}
|
||||
|
||||
type USBConfig struct {
|
||||
Bus string
|
||||
DevNumber string
|
||||
Vendor int
|
||||
Product int
|
||||
}
|
||||
|
||||
// ResourceConfig describes physical attributes of the machine
|
||||
type ResourceConfig struct {
|
||||
// CPUs to be assigned to the VM
|
||||
CPUs uint64
|
||||
// Disk size in gigabytes assigned to the vm
|
||||
DiskSize uint64
|
||||
// Memory in megabytes assigned to the vm
|
||||
Memory uint64
|
||||
// Usbs
|
||||
USBs []USBConfig
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
ReadOnly bool
|
||||
Source string
|
||||
Tag string
|
||||
Target string
|
||||
Type string
|
||||
}
|
||||
|
||||
// ImageConfig describes the bootable image for the VM
|
||||
type ImageConfig struct {
|
||||
// IgnitionFile is the path to the filesystem where the
|
||||
@@ -312,26 +276,6 @@ type ImageConfig struct {
|
||||
ImagePath define.VMFile `json:"ImagePath"`
|
||||
}
|
||||
|
||||
// HostUser describes the host user
|
||||
type HostUser struct {
|
||||
// Whether this machine should run in a rootful or rootless manner
|
||||
Rootful bool
|
||||
// UID is the numerical id of the user that called machine
|
||||
UID int
|
||||
// Whether one of these fields has changed and actions should be taken
|
||||
Modified bool `json:"HostUserModified"`
|
||||
}
|
||||
|
||||
// SSHConfig contains remote access information for SSH
|
||||
type SSHConfig struct {
|
||||
// IdentityPath is the fq path to the ssh priv key
|
||||
IdentityPath string
|
||||
// SSH port for user networking
|
||||
Port int
|
||||
// RemoteUsername of the vm user
|
||||
RemoteUsername string
|
||||
}
|
||||
|
||||
// ConnectionConfig contains connections like sockets, etc.
|
||||
type ConnectionConfig struct {
|
||||
// PodmanSocket is the exported podman service socket
|
||||
|
||||
3
pkg/machine/define/config.go
Normal file
3
pkg/machine/define/config.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package define
|
||||
|
||||
const UserCertsTargetPath = "/etc/containers/certs.d"
|
||||
15
pkg/machine/define/state.go
Normal file
15
pkg/machine/define/state.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package define
|
||||
|
||||
type Status = string
|
||||
|
||||
// Running indicates the qemu vm is running.
|
||||
const Running Status = "running"
|
||||
|
||||
// Stopped indicates the vm has stopped.
|
||||
const Stopped Status = "stopped"
|
||||
|
||||
// Starting indicated the vm is in the process of starting
|
||||
const Starting Status = "starting"
|
||||
|
||||
// Unknown means the state is not known
|
||||
const Unknown Status = "unknown"
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -108,7 +109,7 @@ var _ = Describe("podman machine init", func() {
|
||||
Expect(ec).To(BeZero())
|
||||
Expect(inspectBefore).ToNot(BeEmpty())
|
||||
Expect(inspectAfter).ToNot(BeEmpty())
|
||||
Expect(inspectAfter[0].State).To(Equal(machine.Running))
|
||||
Expect(inspectAfter[0].State).To(Equal(define.Running))
|
||||
|
||||
if isWSL() { // WSL does not use FCOS
|
||||
return
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package e2e_test
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
. "github.com/onsi/gomega/gexec"
|
||||
@@ -32,7 +32,7 @@ var _ = Describe("podman machine start", func() {
|
||||
info, ec, err := mb.toQemuInspectInfo()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ec).To(BeZero())
|
||||
Expect(info[0].State).To(Equal(machine.Running))
|
||||
Expect(info[0].State).To(Equal(define.Running))
|
||||
|
||||
stop := new(stopMachine)
|
||||
stopSession, err := mb.setCmd(stop).run()
|
||||
@@ -77,7 +77,7 @@ var _ = Describe("podman machine start", func() {
|
||||
info, ec, err := mb.toQemuInspectInfo()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ec).To(BeZero())
|
||||
Expect(info[0].State).To(Equal(machine.Running))
|
||||
Expect(info[0].State).To(Equal(define.Running))
|
||||
|
||||
startSession, err = mb.setCmd(s).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -286,14 +286,14 @@ func handlePrevError(e, prevErr error) error {
|
||||
return e
|
||||
}
|
||||
|
||||
func stateConversion(s hypervctl.EnabledState) (machine.Status, error) {
|
||||
func stateConversion(s hypervctl.EnabledState) (define.Status, error) {
|
||||
switch s {
|
||||
case hypervctl.Enabled:
|
||||
return machine.Running, nil
|
||||
return define.Running, nil
|
||||
case hypervctl.Disabled:
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
case hypervctl.Starting:
|
||||
return machine.Starting, nil
|
||||
return define.Starting, nil
|
||||
}
|
||||
return machine.Unknown, fmt.Errorf("unknown state: %q", s.String())
|
||||
return define.Unknown, fmt.Errorf("unknown state: %q", s.String())
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"github.com/containers/libhvee/pkg/hypervctl"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/hyperv/vsock"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v4/pkg/strongunits"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
@@ -99,21 +101,21 @@ type HyperVMachine struct {
|
||||
// ConfigPath is the fully qualified path to the configuration file
|
||||
ConfigPath define.VMFile
|
||||
// HostUser contains info about host user
|
||||
machine.HostUser
|
||||
vmconfigs.HostUser
|
||||
// ImageConfig describes the bootable image
|
||||
machine.ImageConfig
|
||||
// Mounts is the list of remote filesystems to mount
|
||||
Mounts []machine.Mount
|
||||
Mounts []vmconfigs.Mount
|
||||
// Name of VM
|
||||
Name string
|
||||
// NetworkVSock is for the user networking
|
||||
NetworkHVSock HVSockRegistryEntry
|
||||
NetworkHVSock vsock.HVSockRegistryEntry
|
||||
// ReadySocket tells host when vm is booted
|
||||
ReadyHVSock HVSockRegistryEntry
|
||||
ReadyHVSock vsock.HVSockRegistryEntry
|
||||
// ResourceConfig is physical attrs of the VM
|
||||
machine.ResourceConfig
|
||||
vmconfigs.ResourceConfig
|
||||
// SSHConfig for accessing the remote vm
|
||||
machine.SSHConfig
|
||||
vmconfigs.SSHConfig
|
||||
// Starting tells us whether the machine is running or if we have just dialed it to start it
|
||||
Starting bool
|
||||
// Created contains the original created time instead of querying the file mod time
|
||||
@@ -132,11 +134,11 @@ type HyperVMachine struct {
|
||||
// addNetworkAndReadySocketsToRegistry adds the Network and Ready sockets to the
|
||||
// Windows registry
|
||||
func (m *HyperVMachine) addNetworkAndReadySocketsToRegistry() error {
|
||||
networkHVSock, err := NewHVSockRegistryEntry(m.Name, Network)
|
||||
networkHVSock, err := vsock.NewHVSockRegistryEntry(m.Name, vsock.Network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eventHVSocket, err := NewHVSockRegistryEntry(m.Name, Events)
|
||||
eventHVSocket, err := vsock.NewHVSockRegistryEntry(m.Name, vsock.Events)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -185,7 +187,7 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
// around to those, would be another : after that.
|
||||
// TODO: Need to support options here
|
||||
for _, mount := range opts.Volumes {
|
||||
newMount := machine.Mount{}
|
||||
newMount := vmconfigs.Mount{}
|
||||
|
||||
splitMount := strings.Split(mount, ":")
|
||||
if len(splitMount) < 3 {
|
||||
@@ -242,7 +244,7 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
callbackFuncs.Add(m.removeSSHKeys)
|
||||
}
|
||||
|
||||
m.ResourceConfig = machine.ResourceConfig{
|
||||
m.ResourceConfig = vmconfigs.ResourceConfig{
|
||||
CPUs: opts.CPUS,
|
||||
DiskSize: opts.DiskSize,
|
||||
Memory: opts.Memory,
|
||||
@@ -367,7 +369,7 @@ func (m *HyperVMachine) Inspect() (*machine.InspectInfo, error) {
|
||||
},
|
||||
LastUp: m.LastUp,
|
||||
Name: m.Name,
|
||||
Resources: machine.ResourceConfig{
|
||||
Resources: vmconfigs.ResourceConfig{
|
||||
CPUs: uint64(cfg.Hardware.CPUs),
|
||||
DiskSize: 0,
|
||||
Memory: cfg.Hardware.Memory,
|
||||
@@ -543,7 +545,7 @@ func (m *HyperVMachine) SSH(name string, opts machine.SSHOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if state != machine.Running {
|
||||
if state != define.Running {
|
||||
return fmt.Errorf("vm %q is not running", m.Name)
|
||||
}
|
||||
|
||||
@@ -614,21 +616,21 @@ func (m *HyperVMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return m.writeConfig()
|
||||
}
|
||||
|
||||
func (m *HyperVMachine) State(_ bool) (machine.Status, error) {
|
||||
func (m *HyperVMachine) State(_ bool) (define.Status, error) {
|
||||
vmm := hypervctl.NewVirtualMachineManager()
|
||||
vm, err := vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if vm.IsStarting() {
|
||||
return machine.Starting, nil
|
||||
return define.Starting, nil
|
||||
}
|
||||
if vm.State() == hypervctl.Enabled {
|
||||
return machine.Running, nil
|
||||
return define.Running, nil
|
||||
}
|
||||
// Following QEMU pattern here where only three
|
||||
// states seem valid
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
}
|
||||
|
||||
func (m *HyperVMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
@@ -911,19 +913,19 @@ func (m *HyperVMachine) createShares() (_ map[string]uint64, defErr error) {
|
||||
toReturn := make(map[string]uint64)
|
||||
|
||||
for _, mount := range m.Mounts {
|
||||
var vsock *HVSockRegistryEntry
|
||||
var hvSock *vsock.HVSockRegistryEntry
|
||||
|
||||
vsockNum, ok := m.MountVsocks[mount.Target]
|
||||
if ok {
|
||||
// Ignore errors here, we'll just try and recreate the
|
||||
// vsock below.
|
||||
testVsock, err := LoadHVSockRegistryEntry(vsockNum)
|
||||
testVsock, err := vsock.LoadHVSockRegistryEntry(vsockNum)
|
||||
if err == nil {
|
||||
vsock = testVsock
|
||||
hvSock = testVsock
|
||||
}
|
||||
}
|
||||
if vsock == nil {
|
||||
testVsock, err := NewHVSockRegistryEntry(m.Name, Fileserver)
|
||||
if hvSock == nil {
|
||||
testVsock, err := vsock.NewHVSockRegistryEntry(m.Name, vsock.Fileserver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -934,12 +936,12 @@ func (m *HyperVMachine) createShares() (_ map[string]uint64, defErr error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
vsock = testVsock
|
||||
hvSock = testVsock
|
||||
}
|
||||
|
||||
logrus.Debugf("Going to share directory %s via 9p on vsock %d", mount.Source, vsock.Port)
|
||||
logrus.Debugf("Going to share directory %s via 9p on vsock %d", mount.Source, hvSock.Port)
|
||||
|
||||
toReturn[mount.Target] = vsock.Port
|
||||
toReturn[mount.Target] = hvSock.Port
|
||||
}
|
||||
|
||||
return toReturn, nil
|
||||
@@ -955,7 +957,7 @@ func (m *HyperVMachine) removeShares() error {
|
||||
continue
|
||||
}
|
||||
|
||||
vsock, err := LoadHVSockRegistryEntry(vsockNum)
|
||||
vsock, err := vsock.LoadHVSockRegistryEntry(vsockNum)
|
||||
if err != nil {
|
||||
logrus.Debugf("Vsock %d for mountpoint %s does not have a valid registry entry, skipping removal", vsockNum, mount.Target)
|
||||
continue
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package hyperv
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine/sockets"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
@@ -274,7 +275,7 @@ func (hv *HVSockRegistryEntry) Listen() error {
|
||||
}()
|
||||
|
||||
errChan := make(chan error)
|
||||
go machine.ListenAndWaitOnSocket(errChan, listener)
|
||||
go sockets.ListenAndWaitOnSocket(errChan, listener)
|
||||
|
||||
return <-errChan
|
||||
}
|
||||
@@ -10,10 +10,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/libnetwork/etchosts"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -28,7 +25,6 @@ import (
|
||||
*/
|
||||
|
||||
const (
|
||||
UserCertsTargetPath = "/etc/containers/certs.d"
|
||||
PodmanDockerTmpConfPath = "/etc/tmpfiles.d/podman-docker.conf"
|
||||
)
|
||||
|
||||
@@ -615,7 +611,7 @@ func prepareCertFile(path string, name string) (File, error) {
|
||||
return File{}, err
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(UserCertsTargetPath, name)
|
||||
targetPath := filepath.Join(define.UserCertsTargetPath, name)
|
||||
|
||||
logrus.Debugf("Copying cert file from '%s' to '%s'.", path, targetPath)
|
||||
|
||||
@@ -636,22 +632,6 @@ func prepareCertFile(path string, name string) (File, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func GetProxyVariables() map[string]string {
|
||||
proxyOpts := make(map[string]string)
|
||||
for _, variable := range config.ProxyEnv {
|
||||
if value, ok := os.LookupEnv(variable); ok {
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
v := strings.ReplaceAll(value, "127.0.0.1", etchosts.HostContainersInternal)
|
||||
v = strings.ReplaceAll(v, "localhost", etchosts.HostContainersInternal)
|
||||
proxyOpts[variable] = v
|
||||
}
|
||||
}
|
||||
return proxyOpts
|
||||
}
|
||||
|
||||
func getLinks(usrName string) []Link {
|
||||
return []Link{{
|
||||
Node: Node{
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
package qemu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
)
|
||||
|
||||
// QemuCmd is an alias around a string slice to prevent the need to migrate the
|
||||
// MachineVM struct due to changes
|
||||
type QemuCmd []string
|
||||
|
||||
// NewQemuBuilder creates a new QemuCmd object that we will build on top of,
|
||||
// starting with the qemu binary, architecture specific options, and propagated
|
||||
// proxy and SSL settings
|
||||
func NewQemuBuilder(binary string, options []string) QemuCmd {
|
||||
q := QemuCmd{binary}
|
||||
return append(q, options...)
|
||||
}
|
||||
|
||||
// SetMemory adds the specified amount of memory for the machine
|
||||
func (q *QemuCmd) SetMemory(m uint64) {
|
||||
*q = append(*q, "-m", strconv.FormatUint(m, 10))
|
||||
}
|
||||
|
||||
// SetCPUs adds the number of CPUs the machine will have
|
||||
func (q *QemuCmd) SetCPUs(c uint64) {
|
||||
*q = append(*q, "-smp", strconv.FormatUint(c, 10))
|
||||
}
|
||||
|
||||
// SetIgnitionFile specifies the machine's ignition file
|
||||
func (q *QemuCmd) SetIgnitionFile(file define.VMFile) {
|
||||
*q = append(*q, "-fw_cfg", "name=opt/com.coreos/config,file="+file.GetPath())
|
||||
}
|
||||
|
||||
// SetQmpMonitor specifies the machine's qmp socket
|
||||
func (q *QemuCmd) SetQmpMonitor(monitor Monitor) {
|
||||
*q = append(*q, "-qmp", monitor.Network+":"+monitor.Address.GetPath()+",server=on,wait=off")
|
||||
}
|
||||
|
||||
// SetNetwork adds a network device to the machine
|
||||
func (q *QemuCmd) SetNetwork() {
|
||||
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
|
||||
// why we can only run one vm at a time right now
|
||||
*q = append(*q, "-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee")
|
||||
}
|
||||
|
||||
// SetNetwork adds a network device to the machine
|
||||
func (q *QemuCmd) SetUSBHostPassthrough(usbs []machine.USBConfig) {
|
||||
if len(usbs) == 0 {
|
||||
return
|
||||
}
|
||||
// Add xhci usb emulation first and then each usb device
|
||||
*q = append(*q, "-device", "qemu-xhci")
|
||||
for _, usb := range usbs {
|
||||
var dev string
|
||||
if usb.Bus != "" && usb.DevNumber != "" {
|
||||
dev = fmt.Sprintf("usb-host,hostbus=%s,hostaddr=%s", usb.Bus, usb.DevNumber)
|
||||
} else {
|
||||
dev = fmt.Sprintf("usb-host,vendorid=%d,productid=%d", usb.Vendor, usb.Product)
|
||||
}
|
||||
*q = append(*q, "-device", dev)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSerialPort adds a serial port to the machine for readiness
|
||||
func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile define.VMFile, name string) {
|
||||
*q = append(*q,
|
||||
"-device", "virtio-serial",
|
||||
// qemu needs to establish the long name; other connections can use the symlink'd
|
||||
// Note both id and chardev start with an extra "a" because qemu requires that it
|
||||
// starts with a letter but users can also use numbers
|
||||
"-chardev", "socket,path="+readySocket.GetPath()+",server=on,wait=off,id=a"+name+"_ready",
|
||||
"-device", "virtserialport,chardev=a"+name+"_ready"+",name=org.fedoraproject.port.0",
|
||||
"-pidfile", vmPidFile.GetPath())
|
||||
}
|
||||
|
||||
// SetVirtfsMount adds a virtfs mount to the machine
|
||||
func (q *QemuCmd) SetVirtfsMount(source, tag, securityModel string, readonly bool) {
|
||||
virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=%s", source, tag, securityModel)
|
||||
if readonly {
|
||||
virtfsOptions += ",readonly"
|
||||
}
|
||||
*q = append(*q, "-virtfs", virtfsOptions)
|
||||
}
|
||||
|
||||
// SetBootableImage specifies the image the machine will use to boot
|
||||
func (q *QemuCmd) SetBootableImage(image string) {
|
||||
*q = append(*q, "-drive", "if=virtio,file="+image)
|
||||
}
|
||||
|
||||
// SetDisplay specifies whether the machine will have a display
|
||||
func (q *QemuCmd) SetDisplay(display string) {
|
||||
*q = append(*q, "-display", display)
|
||||
}
|
||||
|
||||
// SetPropagatedHostEnvs adds options that propagate SSL and proxy settings
|
||||
func (q *QemuCmd) SetPropagatedHostEnvs() {
|
||||
*q = propagateHostEnv(*q)
|
||||
}
|
||||
|
||||
func (q *QemuCmd) Build() []string {
|
||||
return *q
|
||||
}
|
||||
236
pkg/machine/qemu/command/command.go
Normal file
236
pkg/machine/qemu/command/command.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/common/libnetwork/etchosts"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
)
|
||||
|
||||
// QemuCmd is an alias around a string slice to prevent the need to migrate the
|
||||
// MachineVM struct due to changes
|
||||
type QemuCmd []string
|
||||
|
||||
// NewQemuBuilder creates a new QemuCmd object that we will build on top of,
|
||||
// starting with the qemu binary, architecture specific options, and propagated
|
||||
// proxy and SSL settings
|
||||
func NewQemuBuilder(binary string, options []string) QemuCmd {
|
||||
q := QemuCmd{binary}
|
||||
return append(q, options...)
|
||||
}
|
||||
|
||||
// SetMemory adds the specified amount of memory for the machine
|
||||
func (q *QemuCmd) SetMemory(m uint64) {
|
||||
*q = append(*q, "-m", strconv.FormatUint(m, 10))
|
||||
}
|
||||
|
||||
// SetCPUs adds the number of CPUs the machine will have
|
||||
func (q *QemuCmd) SetCPUs(c uint64) {
|
||||
*q = append(*q, "-smp", strconv.FormatUint(c, 10))
|
||||
}
|
||||
|
||||
// SetIgnitionFile specifies the machine's ignition file
|
||||
func (q *QemuCmd) SetIgnitionFile(file define.VMFile) {
|
||||
*q = append(*q, "-fw_cfg", "name=opt/com.coreos/config,file="+file.GetPath())
|
||||
}
|
||||
|
||||
// SetQmpMonitor specifies the machine's qmp socket
|
||||
func (q *QemuCmd) SetQmpMonitor(monitor Monitor) {
|
||||
*q = append(*q, "-qmp", monitor.Network+":"+monitor.Address.GetPath()+",server=on,wait=off")
|
||||
}
|
||||
|
||||
// SetNetwork adds a network device to the machine
|
||||
func (q *QemuCmd) SetNetwork() {
|
||||
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
|
||||
// why we can only run one vm at a time right now
|
||||
*q = append(*q, "-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee")
|
||||
}
|
||||
|
||||
// SetNetwork adds a network device to the machine
|
||||
func (q *QemuCmd) SetUSBHostPassthrough(usbs []USBConfig) {
|
||||
if len(usbs) == 0 {
|
||||
return
|
||||
}
|
||||
// Add xhci usb emulation first and then each usb device
|
||||
*q = append(*q, "-device", "qemu-xhci")
|
||||
for _, usb := range usbs {
|
||||
var dev string
|
||||
if usb.Bus != "" && usb.DevNumber != "" {
|
||||
dev = fmt.Sprintf("usb-host,hostbus=%s,hostaddr=%s", usb.Bus, usb.DevNumber)
|
||||
} else {
|
||||
dev = fmt.Sprintf("usb-host,vendorid=%d,productid=%d", usb.Vendor, usb.Product)
|
||||
}
|
||||
*q = append(*q, "-device", dev)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSerialPort adds a serial port to the machine for readiness
|
||||
func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile define.VMFile, name string) {
|
||||
*q = append(*q,
|
||||
"-device", "virtio-serial",
|
||||
// qemu needs to establish the long name; other connections can use the symlink'd
|
||||
// Note both id and chardev start with an extra "a" because qemu requires that it
|
||||
// starts with a letter but users can also use numbers
|
||||
"-chardev", "socket,path="+readySocket.GetPath()+",server=on,wait=off,id=a"+name+"_ready",
|
||||
"-device", "virtserialport,chardev=a"+name+"_ready"+",name=org.fedoraproject.port.0",
|
||||
"-pidfile", vmPidFile.GetPath())
|
||||
}
|
||||
|
||||
// SetVirtfsMount adds a virtfs mount to the machine
|
||||
func (q *QemuCmd) SetVirtfsMount(source, tag, securityModel string, readonly bool) {
|
||||
virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=%s", source, tag, securityModel)
|
||||
if readonly {
|
||||
virtfsOptions += ",readonly"
|
||||
}
|
||||
*q = append(*q, "-virtfs", virtfsOptions)
|
||||
}
|
||||
|
||||
// SetBootableImage specifies the image the machine will use to boot
|
||||
func (q *QemuCmd) SetBootableImage(image string) {
|
||||
*q = append(*q, "-drive", "if=virtio,file="+image)
|
||||
}
|
||||
|
||||
// SetDisplay specifies whether the machine will have a display
|
||||
func (q *QemuCmd) SetDisplay(display string) {
|
||||
*q = append(*q, "-display", display)
|
||||
}
|
||||
|
||||
// SetPropagatedHostEnvs adds options that propagate SSL and proxy settings
|
||||
func (q *QemuCmd) SetPropagatedHostEnvs() {
|
||||
*q = propagateHostEnv(*q)
|
||||
}
|
||||
|
||||
func (q *QemuCmd) Build() []string {
|
||||
return *q
|
||||
}
|
||||
|
||||
type USBConfig struct {
|
||||
Bus string
|
||||
DevNumber string
|
||||
Vendor int
|
||||
Product int
|
||||
}
|
||||
|
||||
func ParseUSBs(usbs []string) ([]USBConfig, error) {
|
||||
configs := []USBConfig{}
|
||||
for _, str := range usbs {
|
||||
if str == "" {
|
||||
// Ignore --usb="" as it can be used to reset USBConfigs
|
||||
continue
|
||||
}
|
||||
|
||||
vals := strings.Split(str, ",")
|
||||
if len(vals) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing ',': %s", str)
|
||||
}
|
||||
|
||||
left := strings.Split(vals[0], "=")
|
||||
if len(left) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||
}
|
||||
|
||||
right := strings.Split(vals[1], "=")
|
||||
if len(right) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||
}
|
||||
|
||||
option := left[0] + "_" + right[0]
|
||||
|
||||
switch option {
|
||||
case "bus_devnum", "devnum_bus":
|
||||
bus, devnumber := left[1], right[1]
|
||||
if right[0] == "bus" {
|
||||
bus, devnumber = devnumber, bus
|
||||
}
|
||||
|
||||
configs = append(configs, USBConfig{
|
||||
Bus: bus,
|
||||
DevNumber: devnumber,
|
||||
})
|
||||
case "vendor_product", "product_vendor":
|
||||
vendorStr, productStr := left[1], right[1]
|
||||
if right[0] == "vendor" {
|
||||
vendorStr, productStr = productStr, vendorStr
|
||||
}
|
||||
|
||||
vendor, err := strconv.ParseInt(vendorStr, 16, 0)
|
||||
if err != nil {
|
||||
return configs, fmt.Errorf("usb: fail to convert vendor of %s: %s", str, err)
|
||||
}
|
||||
|
||||
product, err := strconv.ParseInt(productStr, 16, 0)
|
||||
if err != nil {
|
||||
return configs, fmt.Errorf("usb: fail to convert product of %s: %s", str, err)
|
||||
}
|
||||
|
||||
configs = append(configs, USBConfig{
|
||||
Vendor: int(vendor),
|
||||
Product: int(product),
|
||||
})
|
||||
default:
|
||||
return configs, fmt.Errorf("usb: fail to parse: %s", str)
|
||||
}
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func GetProxyVariables() map[string]string {
|
||||
proxyOpts := make(map[string]string)
|
||||
for _, variable := range config.ProxyEnv {
|
||||
if value, ok := os.LookupEnv(variable); ok {
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
v := strings.ReplaceAll(value, "127.0.0.1", etchosts.HostContainersInternal)
|
||||
v = strings.ReplaceAll(v, "localhost", etchosts.HostContainersInternal)
|
||||
proxyOpts[variable] = v
|
||||
}
|
||||
}
|
||||
return proxyOpts
|
||||
}
|
||||
|
||||
// propagateHostEnv is here for providing the ability to propagate
|
||||
// proxy and SSL settings (e.g. HTTP_PROXY and others) on a start
|
||||
// and avoid a need of re-creating/re-initiating a VM
|
||||
func propagateHostEnv(cmdLine QemuCmd) QemuCmd {
|
||||
varsToPropagate := make([]string, 0)
|
||||
|
||||
for k, v := range GetProxyVariables() {
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", k, v))
|
||||
}
|
||||
|
||||
if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok {
|
||||
pathInVM := filepath.Join(define.UserCertsTargetPath, filepath.Base(sslCertFile))
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_FILE", pathInVM))
|
||||
}
|
||||
|
||||
if _, ok := os.LookupEnv("SSL_CERT_DIR"); ok {
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_DIR", define.UserCertsTargetPath))
|
||||
}
|
||||
|
||||
if len(varsToPropagate) > 0 {
|
||||
prefix := "name=opt/com.coreos/environment,string="
|
||||
envVarsJoined := strings.Join(varsToPropagate, "|")
|
||||
fwCfgArg := prefix + base64.StdEncoding.EncodeToString([]byte(envVarsJoined))
|
||||
return append(cmdLine, "-fw_cfg", fwCfgArg)
|
||||
}
|
||||
|
||||
return cmdLine
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
// Address portion of the qmp monitor (/tmp/tmp.sock)
|
||||
Address define.VMFile
|
||||
// Network portion of the qmp monitor (unix)
|
||||
Network string
|
||||
// Timeout in seconds for qmp monitor transactions
|
||||
Timeout time.Duration
|
||||
}
|
||||
94
pkg/machine/qemu/command/command_test.go
Normal file
94
pkg/machine/qemu/command/command_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/common/libnetwork/etchosts"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPropagateHostEnv(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
value string
|
||||
expect string
|
||||
}{
|
||||
"HTTP_PROXY": {
|
||||
"proxy",
|
||||
"equal",
|
||||
},
|
||||
"ftp_proxy": {
|
||||
"domain.com:8888",
|
||||
"equal",
|
||||
},
|
||||
"FTP_PROXY": {
|
||||
"proxy",
|
||||
"equal",
|
||||
},
|
||||
"NO_PROXY": {
|
||||
"localaddress",
|
||||
"equal",
|
||||
},
|
||||
"HTTPS_PROXY": {
|
||||
"",
|
||||
"unset",
|
||||
},
|
||||
"no_proxy": {
|
||||
"",
|
||||
"unset",
|
||||
},
|
||||
"http_proxy": {
|
||||
"127.0.0.1:8888",
|
||||
fmt.Sprintf("%s:8888", etchosts.HostContainersInternal),
|
||||
},
|
||||
"https_proxy": {
|
||||
"localhost:8888",
|
||||
fmt.Sprintf("%s:8888", etchosts.HostContainersInternal),
|
||||
},
|
||||
"SSL_CERT_FILE": {
|
||||
"/some/f=oo.cert",
|
||||
fmt.Sprintf("%s/f=oo.cert", define.UserCertsTargetPath),
|
||||
},
|
||||
"SSL_CERT_DIR": {
|
||||
"/some/my/certs",
|
||||
define.UserCertsTargetPath,
|
||||
},
|
||||
}
|
||||
|
||||
for key, item := range tests {
|
||||
t.Setenv(key, item.value)
|
||||
}
|
||||
|
||||
cmdLine := propagateHostEnv(make([]string, 0))
|
||||
|
||||
assert.Len(t, cmdLine, 2)
|
||||
assert.Equal(t, "-fw_cfg", cmdLine[0])
|
||||
tokens := strings.Split(cmdLine[1], ",string=")
|
||||
decodeString, err := base64.StdEncoding.DecodeString(tokens[1])
|
||||
assert.NoError(t, err)
|
||||
|
||||
// envsRawArr looks like: ["BAR=\"bar\"", "FOO=\"foo\""]
|
||||
envsRawArr := strings.Split(string(decodeString), "|")
|
||||
// envs looks like: {"BAR": "bar", "FOO": "foo"}
|
||||
envs := make(map[string]string)
|
||||
for _, env := range envsRawArr {
|
||||
item := strings.SplitN(env, "=", 2)
|
||||
envs[item[0]] = strings.Trim(item[1], "\"")
|
||||
}
|
||||
|
||||
for key, test := range tests {
|
||||
switch test.expect {
|
||||
case "equal":
|
||||
assert.Equal(t, envs[key], test.value)
|
||||
case "unset":
|
||||
if _, ok := envs[key]; ok {
|
||||
t.Errorf("env %s should not be set", key)
|
||||
}
|
||||
default:
|
||||
assert.Equal(t, envs[key], test.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build (amd64 && !windows) || (arm64 && !windows)
|
||||
// +build amd64,!windows arm64,!windows
|
||||
|
||||
package qemu
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -14,6 +13,9 @@ import (
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/compression"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
"github.com/containers/podman/v4/pkg/machine/sockets"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -59,7 +61,7 @@ func (v *MachineVM) setQMPMonitorSocket() error {
|
||||
// setNewMachineCMD configure the CLI command that will be run to create the new
|
||||
// machine
|
||||
func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCMDOpts) {
|
||||
v.CmdLine = NewQemuBuilder(qemuBinary, v.addArchOptions(cmdOpts))
|
||||
v.CmdLine = command.NewQemuBuilder(qemuBinary, v.addArchOptions(cmdOpts))
|
||||
v.CmdLine.SetMemory(v.Memory)
|
||||
v.CmdLine.SetCPUs(v.CPUs)
|
||||
v.CmdLine.SetIgnitionFile(v.IgnitionFile)
|
||||
@@ -69,69 +71,6 @@ func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCM
|
||||
v.CmdLine.SetUSBHostPassthrough(v.USBs)
|
||||
}
|
||||
|
||||
func parseUSBs(usbs []string) ([]machine.USBConfig, error) {
|
||||
configs := []machine.USBConfig{}
|
||||
for _, str := range usbs {
|
||||
if str == "" {
|
||||
// Ignore --usb="" as it can be used to reset USBConfigs
|
||||
continue
|
||||
}
|
||||
|
||||
vals := strings.Split(str, ",")
|
||||
if len(vals) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing ',': %s", str)
|
||||
}
|
||||
|
||||
left := strings.Split(vals[0], "=")
|
||||
if len(left) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||
}
|
||||
|
||||
right := strings.Split(vals[1], "=")
|
||||
if len(right) != 2 {
|
||||
return configs, fmt.Errorf("usb: fail to parse: missing '=': %s", str)
|
||||
}
|
||||
|
||||
option := left[0] + "_" + right[0]
|
||||
|
||||
switch option {
|
||||
case "bus_devnum", "devnum_bus":
|
||||
bus, devnumber := left[1], right[1]
|
||||
if right[0] == "bus" {
|
||||
bus, devnumber = devnumber, bus
|
||||
}
|
||||
|
||||
configs = append(configs, machine.USBConfig{
|
||||
Bus: bus,
|
||||
DevNumber: devnumber,
|
||||
})
|
||||
case "vendor_product", "product_vendor":
|
||||
vendorStr, productStr := left[1], right[1]
|
||||
if right[0] == "vendor" {
|
||||
vendorStr, productStr = productStr, vendorStr
|
||||
}
|
||||
|
||||
vendor, err := strconv.ParseInt(vendorStr, 16, 0)
|
||||
if err != nil {
|
||||
return configs, fmt.Errorf("usb: fail to convert vendor of %s: %s", str, err)
|
||||
}
|
||||
|
||||
product, err := strconv.ParseInt(productStr, 16, 0)
|
||||
if err != nil {
|
||||
return configs, fmt.Errorf("usb: fail to convert product of %s: %s", str, err)
|
||||
}
|
||||
|
||||
configs = append(configs, machine.USBConfig{
|
||||
Vendor: int(vendor),
|
||||
Product: int(product),
|
||||
})
|
||||
default:
|
||||
return configs, fmt.Errorf("usb: fail to parse: %s", str)
|
||||
}
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// NewMachine initializes an instance of a virtual machine based on the qemu
|
||||
// virtualization.
|
||||
func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||
@@ -169,7 +108,7 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
|
||||
vm.CPUs = opts.CPUS
|
||||
vm.Memory = opts.Memory
|
||||
vm.DiskSize = opts.DiskSize
|
||||
if vm.USBs, err = parseUSBs(opts.USBs); err != nil {
|
||||
if vm.USBs, err = command.ParseUSBs(opts.USBs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -195,7 +134,7 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
|
||||
return nil, err
|
||||
}
|
||||
symlink := vm.Name + "_ready.sock"
|
||||
if err := machine.SetSocket(&vm.ReadySocket, machine.ReadySocketPath(runtimeDir+"/podman/", vm.Name), &symlink); err != nil {
|
||||
if err := sockets.SetSocket(&vm.ReadySocket, sockets.ReadySocketPath(runtimeDir+"/podman/", vm.Name), &symlink); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -209,7 +148,7 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
|
||||
// and returns a vm instance
|
||||
func (p *QEMUVirtualization) LoadVMByName(name string) (machine.VM, error) {
|
||||
vm := &MachineVM{Name: name}
|
||||
vm.HostUser = machine.HostUser{UID: -1} // posix reserves -1, so use it to signify undefined
|
||||
vm.HostUser = vmconfigs.HostUser{UID: -1} // posix reserves -1, so use it to signify undefined
|
||||
if err := vm.update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -274,7 +213,7 @@ func getVMInfos() ([]*machine.ListResponse, error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listEntry.Running = state == machine.Running
|
||||
listEntry.Running = state == define.Running
|
||||
listEntry.LastUp = vm.LastUp
|
||||
|
||||
listed = append(listed, listEntry)
|
||||
|
||||
@@ -4,20 +4,20 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
)
|
||||
|
||||
func TestUSBParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
result []machine.USBConfig
|
||||
result []command.USBConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Good vendor and product",
|
||||
args: []string{"vendor=13d3,product=5406", "vendor=08ec,product=0016"},
|
||||
result: []machine.USBConfig{
|
||||
result: []command.USBConfig{
|
||||
{
|
||||
Vendor: 5075,
|
||||
Product: 21510,
|
||||
@@ -32,7 +32,7 @@ func TestUSBParsing(t *testing.T) {
|
||||
{
|
||||
name: "Good bus and device number",
|
||||
args: []string{"bus=1,devnum=4", "bus=1,devnum=3"},
|
||||
result: []machine.USBConfig{
|
||||
result: []command.USBConfig{
|
||||
{
|
||||
Bus: "1",
|
||||
DevNumber: "4",
|
||||
@@ -47,26 +47,26 @@ func TestUSBParsing(t *testing.T) {
|
||||
{
|
||||
name: "Bad vendor and product, not hexa",
|
||||
args: []string{"vendor=13dk,product=5406"},
|
||||
result: []machine.USBConfig{},
|
||||
result: []command.USBConfig{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Bad vendor and product, bad separator",
|
||||
args: []string{"vendor=13d3:product=5406"},
|
||||
result: []machine.USBConfig{},
|
||||
result: []command.USBConfig{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Bad vendor and product, missing equal",
|
||||
args: []string{"vendor=13d3:product-5406"},
|
||||
result: []machine.USBConfig{},
|
||||
result: []command.USBConfig{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := parseUSBs(test.args)
|
||||
got, err := command.ParseUSBs(test.args)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("parseUUBs error = %v, wantErr %v", err, test.wantErr)
|
||||
return
|
||||
|
||||
@@ -6,7 +6,6 @@ package qemu
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -25,6 +24,9 @@ import (
|
||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
"github.com/containers/podman/v4/pkg/machine/sockets"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v4/pkg/rootless"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
@@ -66,13 +68,13 @@ type MachineVM struct {
|
||||
// ConfigPath is the path to the configuration file
|
||||
ConfigPath define.VMFile
|
||||
// The command line representation of the qemu command
|
||||
CmdLine QemuCmd
|
||||
CmdLine command.QemuCmd
|
||||
// HostUser contains info about host user
|
||||
machine.HostUser
|
||||
vmconfigs.HostUser
|
||||
// ImageConfig describes the bootable image
|
||||
machine.ImageConfig
|
||||
// Mounts is the list of remote filesystems to mount
|
||||
Mounts []machine.Mount
|
||||
Mounts []vmconfigs.Mount
|
||||
// Name of VM
|
||||
Name string
|
||||
// PidFilePath is the where the Proxy PID file lives
|
||||
@@ -80,13 +82,13 @@ type MachineVM struct {
|
||||
// VMPidFilePath is the where the VM PID file lives
|
||||
VMPidFilePath define.VMFile
|
||||
// QMPMonitor is the qemu monitor object for sending commands
|
||||
QMPMonitor Monitor
|
||||
QMPMonitor command.Monitor
|
||||
// ReadySocket tells host when vm is booted
|
||||
ReadySocket define.VMFile
|
||||
// ResourceConfig is physical attrs of the VM
|
||||
machine.ResourceConfig
|
||||
vmconfigs.ResourceConfig
|
||||
// SSHConfig for accessing the remote vm
|
||||
machine.SSHConfig
|
||||
vmconfigs.SSHConfig
|
||||
// Starting tells us whether the machine is running or if we have just dialed it to start it
|
||||
Starting bool
|
||||
// Created contains the original created time instead of querying the file mod time
|
||||
@@ -98,15 +100,6 @@ type MachineVM struct {
|
||||
lock *lockfile.LockFile
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
// Address portion of the qmp monitor (/tmp/tmp.sock)
|
||||
Address define.VMFile
|
||||
// Network portion of the qmp monitor (unix)
|
||||
Network string
|
||||
// Timeout in seconds for qmp monitor transactions
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// addMountsToVM converts the volumes passed through the CLI into the specified
|
||||
// volume driver and adds them to the machine
|
||||
func (v *MachineVM) addMountsToVM(opts machine.InitOptions) error {
|
||||
@@ -119,7 +112,7 @@ func (v *MachineVM) addMountsToVM(opts machine.InitOptions) error {
|
||||
return fmt.Errorf("unknown volume driver: %s", opts.VolumeDriver)
|
||||
}
|
||||
|
||||
mounts := []machine.Mount{}
|
||||
mounts := []vmconfigs.Mount{}
|
||||
for i, volume := range opts.Volumes {
|
||||
tag := fmt.Sprintf("vol%d", i)
|
||||
paths := pathsFromVolume(volume)
|
||||
@@ -128,7 +121,7 @@ func (v *MachineVM) addMountsToVM(opts machine.InitOptions) error {
|
||||
readonly, securityModel := extractMountOptions(paths)
|
||||
if volumeType == VolumeTypeVirtfs {
|
||||
v.CmdLine.SetVirtfsMount(source, tag, securityModel, readonly)
|
||||
mounts = append(mounts, machine.Mount{Type: MountType9p, Tag: tag, Source: source, Target: target, ReadOnly: readonly})
|
||||
mounts = append(mounts, vmconfigs.Mount{Type: MountType9p, Tag: tag, Source: source, Target: target, ReadOnly: readonly})
|
||||
}
|
||||
}
|
||||
v.Mounts = mounts
|
||||
@@ -274,7 +267,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
|
||||
return setErrors, err
|
||||
}
|
||||
|
||||
if state == machine.Running {
|
||||
if state == define.Running {
|
||||
suffix := ""
|
||||
if v.Name != machine.DefaultMachineName {
|
||||
suffix = " " + v.Name
|
||||
@@ -309,7 +302,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
|
||||
}
|
||||
|
||||
if opts.USBs != nil {
|
||||
if usbConfigs, err := parseUSBs(*opts.USBs); err != nil {
|
||||
if usbConfigs, err := command.ParseUSBs(*opts.USBs); err != nil {
|
||||
setErrors = append(setErrors, fmt.Errorf("failed to set usb: %w", err))
|
||||
} else {
|
||||
v.USBs = usbConfigs
|
||||
@@ -381,7 +374,7 @@ func (v *MachineVM) conductVMReadinessCheck(name string, maxBackoffs int, backof
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if state == machine.Running && v.isListening() {
|
||||
if state == define.Running && v.isListening() {
|
||||
// Also make sure that SSH is up and running. The
|
||||
// ready service's dependencies don't fully make sure
|
||||
// that clients can SSH into the machine immediately
|
||||
@@ -469,9 +462,9 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
switch state {
|
||||
case machine.Starting:
|
||||
case define.Starting:
|
||||
return fmt.Errorf("cannot start VM %q: starting state indicates that a previous start has failed: please stop and restart the VM", v.Name)
|
||||
case machine.Running:
|
||||
case define.Running:
|
||||
return fmt.Errorf("cannot start VM %q: %w", v.Name, machine.ErrVMAlreadyRunning)
|
||||
}
|
||||
|
||||
@@ -537,7 +530,7 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
qemuSocketConn, err = machine.DialSocketWithBackoffs(maxBackoffs, defaultBackoff, v.QMPMonitor.Address.Path)
|
||||
qemuSocketConn, err = sockets.DialSocketWithBackoffs(maxBackoffs, defaultBackoff, v.QMPMonitor.Address.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -592,7 +585,7 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
|
||||
fmt.Println("Waiting for VM ...")
|
||||
}
|
||||
|
||||
conn, err = machine.DialSocketWithBackoffsAndProcCheck(maxBackoffs, defaultBackoff, v.ReadySocket.GetPath(), checkProcessStatus, "qemu", cmd.Process.Pid, stderrBuf)
|
||||
conn, err = sockets.DialSocketWithBackoffsAndProcCheck(maxBackoffs, defaultBackoff, v.ReadySocket.GetPath(), checkProcessStatus, "qemu", cmd.Process.Pid, stderrBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -656,36 +649,7 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// propagateHostEnv is here for providing the ability to propagate
|
||||
// proxy and SSL settings (e.g. HTTP_PROXY and others) on a start
|
||||
// and avoid a need of re-creating/re-initiating a VM
|
||||
func propagateHostEnv(cmdLine QemuCmd) QemuCmd {
|
||||
varsToPropagate := make([]string, 0)
|
||||
|
||||
for k, v := range machine.GetProxyVariables() {
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", k, v))
|
||||
}
|
||||
|
||||
if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok {
|
||||
pathInVM := filepath.Join(machine.UserCertsTargetPath, filepath.Base(sslCertFile))
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_FILE", pathInVM))
|
||||
}
|
||||
|
||||
if _, ok := os.LookupEnv("SSL_CERT_DIR"); ok {
|
||||
varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_DIR", machine.UserCertsTargetPath))
|
||||
}
|
||||
|
||||
if len(varsToPropagate) > 0 {
|
||||
prefix := "name=opt/com.coreos/environment,string="
|
||||
envVarsJoined := strings.Join(varsToPropagate, "|")
|
||||
fwCfgArg := prefix + base64.StdEncoding.EncodeToString([]byte(envVarsJoined))
|
||||
return append(cmdLine, "-fw_cfg", fwCfgArg)
|
||||
}
|
||||
|
||||
return cmdLine
|
||||
}
|
||||
|
||||
func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, error) {
|
||||
func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (define.Status, error) {
|
||||
// this is the format returned from the monitor
|
||||
// {"return": {"status": "running", "singlestep": false, "running": true}}
|
||||
|
||||
@@ -712,17 +676,17 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, err
|
||||
b, err := monitor.Run(input)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if err := json.Unmarshal(b, &response); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if response.Response.Status == machine.Running {
|
||||
return machine.Running, nil
|
||||
if response.Response.Status == define.Running {
|
||||
return define.Running, nil
|
||||
}
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
}
|
||||
|
||||
// waitForMachineToStop waits for the machine to stop running
|
||||
@@ -734,7 +698,7 @@ func (v *MachineVM) waitForMachineToStop() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if state != machine.Running {
|
||||
if state != define.Running {
|
||||
break
|
||||
}
|
||||
time.Sleep(waitInternal)
|
||||
@@ -929,10 +893,10 @@ func (v *MachineVM) stopLocked() error {
|
||||
}
|
||||
|
||||
// NewQMPMonitor creates the monitor subsection of our vm
|
||||
func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) {
|
||||
func NewQMPMonitor(network, name string, timeout time.Duration) (command.Monitor, error) {
|
||||
rtDir, err := getRuntimeDir()
|
||||
if err != nil {
|
||||
return Monitor{}, err
|
||||
return command.Monitor{}, err
|
||||
}
|
||||
if isRootful() {
|
||||
rtDir = "/run"
|
||||
@@ -940,7 +904,7 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
|
||||
rtDir = filepath.Join(rtDir, "podman")
|
||||
if _, err := os.Stat(rtDir); errors.Is(err, fs.ErrNotExist) {
|
||||
if err := os.MkdirAll(rtDir, 0755); err != nil {
|
||||
return Monitor{}, err
|
||||
return command.Monitor{}, err
|
||||
}
|
||||
}
|
||||
if timeout == 0 {
|
||||
@@ -948,9 +912,9 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
|
||||
}
|
||||
address, err := define.NewMachineFile(filepath.Join(rtDir, "qmp_"+name+".sock"), nil)
|
||||
if err != nil {
|
||||
return Monitor{}, err
|
||||
return command.Monitor{}, err
|
||||
}
|
||||
monitor := Monitor{
|
||||
monitor := command.Monitor{
|
||||
Network: network,
|
||||
Address: *address,
|
||||
Timeout: timeout,
|
||||
@@ -1021,7 +985,7 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if state == machine.Running {
|
||||
if state == define.Running {
|
||||
if !opts.Force {
|
||||
return "", nil, &machine.ErrVMRunningCannotDestroyed{Name: v.Name}
|
||||
}
|
||||
@@ -1050,7 +1014,7 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||
func (v *MachineVM) State(bypass bool) (define.Status, error) {
|
||||
// Check if qmp socket path exists
|
||||
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); errors.Is(err, fs.ErrNotExist) {
|
||||
return "", nil
|
||||
@@ -1061,7 +1025,7 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||
}
|
||||
// Check if we can dial it
|
||||
if v.Starting && !bypass {
|
||||
return machine.Starting, nil
|
||||
return define.Starting, nil
|
||||
}
|
||||
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
|
||||
if err != nil {
|
||||
@@ -1069,7 +1033,7 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||
// 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 define.Stopped, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
@@ -1102,7 +1066,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if state != machine.Running {
|
||||
if state != define.Running {
|
||||
return fmt.Errorf("vm %q is not running", v.Name)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,105 +4,18 @@
|
||||
package qemu
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/common/libnetwork/etchosts"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEditCmd(t *testing.T) {
|
||||
vm := new(MachineVM)
|
||||
vm.CmdLine = QemuCmd{"command", "-flag", "value"}
|
||||
vm.CmdLine = command.QemuCmd{"command", "-flag", "value"}
|
||||
|
||||
vm.editCmdLine("-flag", "newvalue")
|
||||
vm.editCmdLine("-anotherflag", "anothervalue")
|
||||
|
||||
require.Equal(t, vm.CmdLine.Build(), []string{"command", "-flag", "newvalue", "-anotherflag", "anothervalue"})
|
||||
}
|
||||
|
||||
func TestPropagateHostEnv(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
value string
|
||||
expect string
|
||||
}{
|
||||
"HTTP_PROXY": {
|
||||
"proxy",
|
||||
"equal",
|
||||
},
|
||||
"ftp_proxy": {
|
||||
"domain.com:8888",
|
||||
"equal",
|
||||
},
|
||||
"FTP_PROXY": {
|
||||
"proxy",
|
||||
"equal",
|
||||
},
|
||||
"NO_PROXY": {
|
||||
"localaddress",
|
||||
"equal",
|
||||
},
|
||||
"HTTPS_PROXY": {
|
||||
"",
|
||||
"unset",
|
||||
},
|
||||
"no_proxy": {
|
||||
"",
|
||||
"unset",
|
||||
},
|
||||
"http_proxy": {
|
||||
"127.0.0.1:8888",
|
||||
fmt.Sprintf("%s:8888", etchosts.HostContainersInternal),
|
||||
},
|
||||
"https_proxy": {
|
||||
"localhost:8888",
|
||||
fmt.Sprintf("%s:8888", etchosts.HostContainersInternal),
|
||||
},
|
||||
"SSL_CERT_FILE": {
|
||||
"/some/f=oo.cert",
|
||||
fmt.Sprintf("%s/f=oo.cert", machine.UserCertsTargetPath),
|
||||
},
|
||||
"SSL_CERT_DIR": {
|
||||
"/some/my/certs",
|
||||
machine.UserCertsTargetPath,
|
||||
},
|
||||
}
|
||||
|
||||
for key, item := range tests {
|
||||
t.Setenv(key, item.value)
|
||||
}
|
||||
|
||||
cmdLine := propagateHostEnv(make([]string, 0))
|
||||
|
||||
assert.Len(t, cmdLine, 2)
|
||||
assert.Equal(t, "-fw_cfg", cmdLine[0])
|
||||
tokens := strings.Split(cmdLine[1], ",string=")
|
||||
decodeString, err := base64.StdEncoding.DecodeString(tokens[1])
|
||||
assert.NoError(t, err)
|
||||
|
||||
// envsRawArr looks like: ["BAR=\"bar\"", "FOO=\"foo\""]
|
||||
envsRawArr := strings.Split(string(decodeString), "|")
|
||||
// envs looks like: {"BAR": "bar", "FOO": "foo"}
|
||||
envs := make(map[string]string)
|
||||
for _, env := range envsRawArr {
|
||||
item := strings.SplitN(env, "=", 2)
|
||||
envs[item[0]] = strings.Trim(item[1], "\"")
|
||||
}
|
||||
|
||||
for key, test := range tests {
|
||||
switch test.expect {
|
||||
case "equal":
|
||||
assert.Equal(t, envs[key], test.value)
|
||||
case "unset":
|
||||
if _, ok := envs[key]; ok {
|
||||
t.Errorf("env %s should not be set", key)
|
||||
}
|
||||
default:
|
||||
assert.Equal(t, envs[key], test.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package machine
|
||||
package sockets
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
142
pkg/machine/vmconfigs/config.go
Normal file
142
pkg/machine/vmconfigs/config.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package vmconfigs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
)
|
||||
|
||||
type aThing struct{}
|
||||
|
||||
type MachineConfig struct {
|
||||
// Common stuff
|
||||
Created time.Time
|
||||
GvProxy gvproxy.GvproxyCommand
|
||||
HostUser HostUser
|
||||
IgnitionFile *aThing // possible interface
|
||||
LastUp time.Time
|
||||
LogPath *define.VMFile `json:",omitempty"` // Revisit this for all providers
|
||||
Mounts []Mount
|
||||
Name string
|
||||
ReadySocket *aThing // possible interface
|
||||
Resources ResourceConfig
|
||||
SSH SSHConfig
|
||||
Starting *bool
|
||||
Version uint
|
||||
|
||||
// Image stuff
|
||||
imageDescription machineImage //nolint:unused
|
||||
|
||||
// Provider stuff
|
||||
AppleHypervisor *AppleHVConfig `json:",omitempty"`
|
||||
QEMUHypervisor *QEMUConfig `json:",omitempty"`
|
||||
HyperVHypervisor *HyperVConfig `json:",omitempty"`
|
||||
WSLHypervisor *WSLConfig `json:",omitempty"`
|
||||
|
||||
lock *lockfile.LockFile //nolint:unused
|
||||
}
|
||||
|
||||
// MachineImage describes a podman machine image
|
||||
type MachineImage struct {
|
||||
OCI *ociMachineImage
|
||||
FCOS *fcosMachineImage
|
||||
}
|
||||
|
||||
// Pull downloads a machine image
|
||||
func (m *MachineImage) Pull() error {
|
||||
if m.OCI != nil {
|
||||
return m.OCI.download()
|
||||
}
|
||||
if m.FCOS != nil {
|
||||
return m.FCOS.download()
|
||||
}
|
||||
return errors.New("no valid machine image provider detected")
|
||||
}
|
||||
|
||||
type machineImage interface { //nolint:unused
|
||||
download() error
|
||||
path() string
|
||||
}
|
||||
|
||||
type ociMachineImage struct {
|
||||
// registry
|
||||
// TODO JSON serial/deserial will write string to disk
|
||||
// but in code it is a types.ImageReference
|
||||
|
||||
// quay.io/podman/podman-machine-image:5.0
|
||||
FQImageReference string
|
||||
}
|
||||
|
||||
func (o ociMachineImage) path() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (o ociMachineImage) download() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fcosMachineImage struct {
|
||||
// TODO JSON serial/deserial will write string to disk
|
||||
// but in code is url.URL
|
||||
Location url.URL // file://path/.qcow2 https://path/qcow2
|
||||
}
|
||||
|
||||
func (f fcosMachineImage) download() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fcosMachineImage) path() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// HostUser describes the host user
|
||||
type HostUser struct {
|
||||
// Whether this machine should run in a rootful or rootless manner
|
||||
Rootful bool
|
||||
// UID is the numerical id of the user that called machine
|
||||
UID int
|
||||
// Whether one of these fields has changed and actions should be taken
|
||||
Modified bool `json:"HostUserModified"`
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
ReadOnly bool
|
||||
Source string
|
||||
Tag string
|
||||
Target string
|
||||
Type string
|
||||
}
|
||||
|
||||
// ResourceConfig describes physical attributes of the machine
|
||||
type ResourceConfig struct {
|
||||
// CPUs to be assigned to the VM
|
||||
CPUs uint64
|
||||
// Disk size in gigabytes assigned to the vm
|
||||
DiskSize uint64
|
||||
// Memory in megabytes assigned to the vm
|
||||
Memory uint64
|
||||
// Usbs
|
||||
USBs []command.USBConfig
|
||||
}
|
||||
|
||||
// SSHConfig contains remote access information for SSH
|
||||
type SSHConfig struct {
|
||||
// IdentityPath is the fq path to the ssh priv key
|
||||
IdentityPath string
|
||||
// SSH port for user networking
|
||||
Port int
|
||||
// RemoteUsername of the vm user
|
||||
RemoteUsername string
|
||||
}
|
||||
|
||||
type VMStats struct {
|
||||
// Created contains the original created time instead of querying the file mod time
|
||||
Created time.Time
|
||||
// LastUp contains the last recorded uptime
|
||||
LastUp time.Time
|
||||
}
|
||||
15
pkg/machine/vmconfigs/config_darwin.go
Normal file
15
pkg/machine/vmconfigs/config_darwin.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package vmconfigs
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/machine/applehv/vfkit"
|
||||
)
|
||||
|
||||
type AppleHVConfig struct {
|
||||
// The VFKit endpoint where we can interact with the VM
|
||||
Vfkit vfkit.VfkitHelper
|
||||
}
|
||||
|
||||
// Stubs
|
||||
type HyperVConfig struct{}
|
||||
type WSLConfig struct{}
|
||||
type QEMUConfig struct{}
|
||||
7
pkg/machine/vmconfigs/config_freebsd.go
Normal file
7
pkg/machine/vmconfigs/config_freebsd.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package vmconfigs
|
||||
|
||||
// Stubs
|
||||
type HyperVConfig struct{}
|
||||
type WSLConfig struct {}
|
||||
type QEMUConfig struct {}
|
||||
type AppleHVConfig struct {}
|
||||
14
pkg/machine/vmconfigs/config_linux.go
Normal file
14
pkg/machine/vmconfigs/config_linux.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vmconfigs
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/machine/qemu/command"
|
||||
)
|
||||
|
||||
type QEMUConfig struct {
|
||||
cmd command.QemuCmd //nolint:unused
|
||||
}
|
||||
|
||||
// Stubs
|
||||
type AppleHVConfig struct{}
|
||||
type HyperVConfig struct{}
|
||||
type WSLConfig struct{}
|
||||
21
pkg/machine/vmconfigs/config_windows.go
Normal file
21
pkg/machine/vmconfigs/config_windows.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package vmconfigs
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/machine/hyperv/vsock"
|
||||
)
|
||||
|
||||
type HyperVConfig struct {
|
||||
// NetworkVSock is for the user networking
|
||||
NetworkHVSock vsock.HVSockRegistryEntry
|
||||
// MountVsocks contains the currently-active vsocks, mapped to the
|
||||
// directory they should be mounted on.
|
||||
MountVsocks map[string]uint64
|
||||
}
|
||||
|
||||
type WSLConfig struct {
|
||||
wslstuff *aThing
|
||||
}
|
||||
|
||||
// Stubs
|
||||
type QEMUConfig struct{}
|
||||
type AppleHVConfig struct{}
|
||||
@@ -2,6 +2,8 @@ package machine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
)
|
||||
|
||||
type Volume interface {
|
||||
@@ -37,8 +39,8 @@ func (v VirtIoFs) unitName() string {
|
||||
return unit
|
||||
}
|
||||
|
||||
func (v VirtIoFs) ToMount() Mount {
|
||||
return Mount{
|
||||
func (v VirtIoFs) ToMount() vmconfigs.Mount {
|
||||
return vmconfigs.Mount{
|
||||
ReadOnly: v.ReadOnly,
|
||||
Source: v.Source,
|
||||
Tag: v.Tag,
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/containers/podman/v4/pkg/machine/define"
|
||||
"github.com/containers/podman/v4/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v4/pkg/machine/wsl/wutil"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/containers/podman/v4/utils"
|
||||
@@ -298,7 +299,7 @@ type MachineVM struct {
|
||||
// Whether this machine should run in a rootful or rootless manner
|
||||
Rootful bool
|
||||
// SSH identity, username, etc
|
||||
machine.SSHConfig
|
||||
vmconfigs.SSHConfig
|
||||
// machine version
|
||||
Version int
|
||||
// Whether to use user-mode networking
|
||||
@@ -1526,12 +1527,12 @@ func unregisterDist(dist string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||
func (v *MachineVM) State(bypass bool) (define.Status, error) {
|
||||
if v.isRunning() {
|
||||
return machine.Running, nil
|
||||
return define.Running, nil
|
||||
}
|
||||
|
||||
return machine.Stopped, nil
|
||||
return define.Stopped, nil
|
||||
}
|
||||
|
||||
func stopWinProxy(v *MachineVM) error {
|
||||
@@ -1808,7 +1809,7 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
|
||||
machinePipe := toDist(v.Name)
|
||||
connInfo.PodmanPipe = &define.VMFile{Path: `\\.\pipe\` + machinePipe}
|
||||
|
||||
created, lastUp, _ := v.updateTimeStamps(state == machine.Running)
|
||||
created, lastUp, _ := v.updateTimeStamps(state == define.Running)
|
||||
return &machine.InspectInfo{
|
||||
ConfigPath: define.VMFile{Path: v.ConfigPath},
|
||||
ConnectionInfo: *connInfo,
|
||||
@@ -1827,7 +1828,7 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *MachineVM) getResources() (resources machine.ResourceConfig) {
|
||||
func (v *MachineVM) getResources() (resources vmconfigs.ResourceConfig) {
|
||||
resources.CPUs, _ = getCPUs(v)
|
||||
resources.Memory, _ = getMem(v)
|
||||
resources.DiskSize = getDiskSize(v)
|
||||
|
||||
Reference in New Issue
Block a user