diff --git a/cmd/podman/compose.go b/cmd/podman/compose.go index 5f4bc64d6f..5dff6150dc 100644 --- a/cmd/podman/compose.go +++ b/cmd/podman/compose.go @@ -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 { diff --git a/pkg/machine/applehv/config.go b/pkg/machine/applehv/config.go index c04b43e22a..91e5cc899a 100644 --- a/pkg/machine/applehv/config.go +++ b/pkg/machine/applehv/config.go @@ -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 diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index 6e628c873b..c3f8d812fc 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -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) diff --git a/pkg/machine/applehv/rest.go b/pkg/machine/applehv/vfkit/config.go similarity index 71% rename from pkg/machine/applehv/rest.go rename to pkg/machine/applehv/vfkit/config.go index a96834541f..b3fa72accc 100644 --- a/pkg/machine/applehv/rest.go +++ b/pkg/machine/applehv/vfkit/config.go @@ -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 +} diff --git a/pkg/machine/applehv/rest_config.go b/pkg/machine/applehv/vfkit/rest.go similarity index 85% rename from pkg/machine/applehv/rest_config.go rename to pkg/machine/applehv/vfkit/rest.go index 944e72d539..b365310a2c 100644 --- a/pkg/machine/applehv/rest_config.go +++ b/pkg/machine/applehv/vfkit/rest.go @@ -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") } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index b4324e8755..12383aa688 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -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 diff --git a/pkg/machine/define/config.go b/pkg/machine/define/config.go new file mode 100644 index 0000000000..ba98908be1 --- /dev/null +++ b/pkg/machine/define/config.go @@ -0,0 +1,3 @@ +package define + +const UserCertsTargetPath = "/etc/containers/certs.d" diff --git a/pkg/machine/define/state.go b/pkg/machine/define/state.go new file mode 100644 index 0000000000..1817803466 --- /dev/null +++ b/pkg/machine/define/state.go @@ -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" diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index db717e3a84..4aab924466 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -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 diff --git a/pkg/machine/e2e/start_test.go b/pkg/machine/e2e/start_test.go index b62ff9396f..d1001a45d2 100644 --- a/pkg/machine/e2e/start_test.go +++ b/pkg/machine/e2e/start_test.go @@ -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()) diff --git a/pkg/machine/hyperv/config.go b/pkg/machine/hyperv/config.go index af4e47ccda..8b8d2336ce 100644 --- a/pkg/machine/hyperv/config.go +++ b/pkg/machine/hyperv/config.go @@ -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()) } diff --git a/pkg/machine/hyperv/machine.go b/pkg/machine/hyperv/machine.go index aa5c8a2ebc..272bf04ebb 100644 --- a/pkg/machine/hyperv/machine.go +++ b/pkg/machine/hyperv/machine.go @@ -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 diff --git a/pkg/machine/hyperv/vsock.go b/pkg/machine/hyperv/vsock/vsock.go similarity index 98% rename from pkg/machine/hyperv/vsock.go rename to pkg/machine/hyperv/vsock/vsock.go index 178694002f..f4789201bb 100644 --- a/pkg/machine/hyperv/vsock.go +++ b/pkg/machine/hyperv/vsock/vsock.go @@ -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 } diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 9eaafe2faf..dc6ef05230 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -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{ diff --git a/pkg/machine/qemu/command.go b/pkg/machine/qemu/command.go deleted file mode 100644 index c8dc315106..0000000000 --- a/pkg/machine/qemu/command.go +++ /dev/null @@ -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 -} diff --git a/pkg/machine/qemu/command/command.go b/pkg/machine/qemu/command/command.go new file mode 100644 index 0000000000..3619619ef3 --- /dev/null +++ b/pkg/machine/qemu/command/command.go @@ -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 +} diff --git a/pkg/machine/qemu/command/command_test.go b/pkg/machine/qemu/command/command_test.go new file mode 100644 index 0000000000..e2307f3693 --- /dev/null +++ b/pkg/machine/qemu/command/command_test.go @@ -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) + } + } +} diff --git a/pkg/machine/qemu/qemu_command_test.go b/pkg/machine/qemu/command/qemu_command_test.go similarity index 99% rename from pkg/machine/qemu/qemu_command_test.go rename to pkg/machine/qemu/command/qemu_command_test.go index 5041dcb156..ed198f2a0f 100644 --- a/pkg/machine/qemu/qemu_command_test.go +++ b/pkg/machine/qemu/command/qemu_command_test.go @@ -1,7 +1,7 @@ //go:build (amd64 && !windows) || (arm64 && !windows) // +build amd64,!windows arm64,!windows -package qemu +package command import ( "fmt" diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 25d5af5e7c..a47b77fe2b 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -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) diff --git a/pkg/machine/qemu/config_test.go b/pkg/machine/qemu/config_test.go index d1bd0f291e..ae630f355c 100644 --- a/pkg/machine/qemu/config_test.go +++ b/pkg/machine/qemu/config_test.go @@ -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 diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index dac5b1a873..9a74113a44 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -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) } diff --git a/pkg/machine/qemu/machine_test.go b/pkg/machine/qemu/machine_test.go index dbf2ebaa68..354ea5b688 100644 --- a/pkg/machine/qemu/machine_test.go +++ b/pkg/machine/qemu/machine_test.go @@ -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) - } - } -} diff --git a/pkg/machine/sockets.go b/pkg/machine/sockets/sockets.go similarity index 99% rename from pkg/machine/sockets.go rename to pkg/machine/sockets/sockets.go index a7d51061cb..6d966dbfb3 100644 --- a/pkg/machine/sockets.go +++ b/pkg/machine/sockets/sockets.go @@ -1,4 +1,4 @@ -package machine +package sockets import ( "bufio" diff --git a/pkg/machine/vmconfigs/config.go b/pkg/machine/vmconfigs/config.go new file mode 100644 index 0000000000..8a4cb1e718 --- /dev/null +++ b/pkg/machine/vmconfigs/config.go @@ -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 +} diff --git a/pkg/machine/vmconfigs/config_darwin.go b/pkg/machine/vmconfigs/config_darwin.go new file mode 100644 index 0000000000..62bdff414e --- /dev/null +++ b/pkg/machine/vmconfigs/config_darwin.go @@ -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{} diff --git a/pkg/machine/vmconfigs/config_freebsd.go b/pkg/machine/vmconfigs/config_freebsd.go new file mode 100644 index 0000000000..1970769ff4 --- /dev/null +++ b/pkg/machine/vmconfigs/config_freebsd.go @@ -0,0 +1,7 @@ +package vmconfigs + +// Stubs +type HyperVConfig struct{} +type WSLConfig struct {} +type QEMUConfig struct {} +type AppleHVConfig struct {} diff --git a/pkg/machine/vmconfigs/config_linux.go b/pkg/machine/vmconfigs/config_linux.go new file mode 100644 index 0000000000..59d37e8f44 --- /dev/null +++ b/pkg/machine/vmconfigs/config_linux.go @@ -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{} diff --git a/pkg/machine/vmconfigs/config_windows.go b/pkg/machine/vmconfigs/config_windows.go new file mode 100644 index 0000000000..8cec6976ea --- /dev/null +++ b/pkg/machine/vmconfigs/config_windows.go @@ -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{} diff --git a/pkg/machine/volumes.go b/pkg/machine/volumes.go index 5f7eaf07de..b2a9d9de41 100644 --- a/pkg/machine/volumes.go +++ b/pkg/machine/volumes.go @@ -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, diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index e97baf51cb..10bf00e794 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -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)