mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
Podman 5 machine config file - Step 1
The following PR is the very first step in what will a series of steps to apply a "common" machine configuration file to all providers. Function names, method names, struct names, and field names are all up for debate. The purpose of this PR is to offer a glimpse at the direction we intend to take. This PR also contains temporary structs (i.e. aThing) that are not exported. These are merely placeholders. The configuration work in this PR is also unused of yet. But the code is compiled. Once merged, we can begin the next step of development. [NO NEW TESTS NEEDED] Signed-off-by: Brent Baude <bbaude@redhat.com>
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