From 6b02c4894b0e13ef8fb15b53d3c52133f324cf01 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Mon, 22 Jan 2024 15:21:37 -0600 Subject: [PATCH] Podman 5 machine refactor - applehv this is the second provider done (qemu first). all tests pass on arm64 hardware locally ... the hybrid pull from oci registries limit this to arm64 only. calling gvproxy, waiting for it, and then vfkit seems to still be problematic. this would be an area that should be cleaned up once all providers are implemented. Signed-off-by: Brent Baude --- pkg/machine/applehv/claim.go | 83 -- pkg/machine/applehv/config.go | 193 ----- pkg/machine/applehv/ignition.go | 14 +- pkg/machine/applehv/machine.go | 1043 +---------------------- pkg/machine/applehv/stubber.go | 311 +++++++ pkg/machine/applehv/vfkit.go | 18 +- pkg/machine/applehv/vfkit/config.go | 7 +- pkg/machine/define/vmfile.go | 3 +- pkg/machine/e2e/machine_test.go | 14 +- pkg/machine/e2e/pull_test.go | 27 +- pkg/machine/provider/platform_darwin.go | 9 +- pkg/machine/qemu/stubber.go | 4 +- pkg/machine/shim/claim_unsupported.go | 2 +- pkg/machine/shim/host.go | 15 +- pkg/machine/shim/networking.go | 3 + pkg/machine/shim/volume.go | 30 + pkg/machine/sockets/sockets.go | 3 + pkg/machine/vmconfigs/config.go | 3 +- pkg/machine/vmconfigs/machine.go | 24 +- pkg/machine/vmconfigs/sockets.go | 17 + pkg/machine/vmconfigs/sockets_darwin.go | 17 + pkg/machine/vmconfigs/volumes.go | 17 - pkg/machine/volumes.go | 10 + 23 files changed, 499 insertions(+), 1368 deletions(-) delete mode 100644 pkg/machine/applehv/claim.go create mode 100644 pkg/machine/applehv/stubber.go create mode 100644 pkg/machine/shim/volume.go create mode 100644 pkg/machine/vmconfigs/sockets.go create mode 100644 pkg/machine/vmconfigs/sockets_darwin.go diff --git a/pkg/machine/applehv/claim.go b/pkg/machine/applehv/claim.go deleted file mode 100644 index c02f15d839..0000000000 --- a/pkg/machine/applehv/claim.go +++ /dev/null @@ -1,83 +0,0 @@ -//go:build darwin - -package applehv - -import ( - "fmt" - "io" - "io/fs" - "net" - "os" - "os/user" - "path/filepath" - "time" -) - -// TODO the following functions were taken from pkg/qemu/claim_darwin.go and -// should be refactored. I'm thinking even something in pkg/machine/ - -func dockerClaimSupported() bool { - return true -} - -func dockerClaimHelperInstalled() bool { - u, err := user.Current() - if err != nil { - return false - } - - labelName := fmt.Sprintf("com.github.containers.podman.helper-%s", u.Username) - fileName := filepath.Join("/Library", "LaunchDaemons", labelName+".plist") - info, err := os.Stat(fileName) - return err == nil && info.Mode().IsRegular() -} - -func claimDockerSock() bool { - u, err := user.Current() - if err != nil { - return false - } - - helperSock := fmt.Sprintf("/var/run/podman-helper-%s.socket", u.Username) - con, err := net.DialTimeout("unix", helperSock, time.Second*5) - if err != nil { - return false - } - _ = con.SetWriteDeadline(time.Now().Add(time.Second * 5)) - _, err = fmt.Fprintln(con, "GO") - if err != nil { - return false - } - _ = con.SetReadDeadline(time.Now().Add(time.Second * 5)) - read, err := io.ReadAll(con) - - return err == nil && string(read) == "OK" -} - -func findClaimHelper() string { - exe, err := os.Executable() - if err != nil { - return "" - } - - exe, err = filepath.EvalSymlinks(exe) - if err != nil { - return "" - } - - return filepath.Join(filepath.Dir(exe), "podman-mac-helper") -} - -func checkSockInUse(sock string) bool { - if info, err := os.Stat(sock); err == nil && info.Mode()&fs.ModeSocket == fs.ModeSocket { - _, err = net.DialTimeout("unix", dockerSock, dockerConnectTimeout) - return err == nil - } - - return false -} - -func alreadyLinked(target string, link string) bool { - read, err := os.Readlink(link) - return err == nil && read == target -} diff --git a/pkg/machine/applehv/config.go b/pkg/machine/applehv/config.go index 60149c34df..f5d509598a 100644 --- a/pkg/machine/applehv/config.go +++ b/pkg/machine/applehv/config.go @@ -2,200 +2,7 @@ package applehv -import ( - "errors" - "fmt" - "io/fs" - "path/filepath" - "time" - - "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/ignition" - "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" -) - const ( localhostURI = "http://localhost" ignitionSocketName = "ignition.sock" ) - -type AppleHVVirtualization struct { - machine.Virtualization -} - -type MMHardwareConfig struct { - CPUs uint16 - DiskPath string - DiskSize uint64 - Memory int32 -} - -func VirtualizationProvider() machine.VirtProvider { - return &AppleHVVirtualization{ - machine.NewVirtualization(define.AppleHV, compression.Xz, define.Raw, vmtype), - } -} - -func (v AppleHVVirtualization) CheckExclusiveActiveVM() (bool, string, error) { - fsVms, err := getVMInfos() - if err != nil { - return false, "", err - } - for _, vm := range fsVms { - if vm.Running || vm.Starting { - return true, vm.Name, nil - } - } - - return false, "", nil -} - -func (v AppleHVVirtualization) IsValidVMName(name string) (bool, error) { - configDir, err := machine.GetConfDir(define.AppleHvVirt) - if err != nil { - return false, err - } - fqName := filepath.Join(configDir, fmt.Sprintf("%s.json", name)) - if _, err := loadMacMachineFromJSON(fqName); err != nil { - return false, err - } - return true, nil -} - -func (v AppleHVVirtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) { - var ( - response []*machine.ListResponse - ) - - mms, err := v.loadFromLocalJson() - if err != nil { - return nil, err - } - - for _, mm := range mms { - vmState, err := mm.Vfkit.State() - if err != nil { - if errors.Is(err, unix.ECONNREFUSED) { - vmState = define.Stopped - } else { - return nil, err - } - } - - mlr := machine.ListResponse{ - Name: mm.Name, - CreatedAt: mm.Created, - LastUp: mm.LastUp, - Running: vmState == define.Running, - Starting: vmState == define.Starting, - Stream: mm.ImageStream, - VMType: define.AppleHvVirt.String(), - CPUs: mm.CPUs, - Memory: mm.Memory * units.MiB, - DiskSize: mm.DiskSize * units.GiB, - Port: mm.Port, - RemoteUsername: mm.RemoteUsername, - IdentityPath: mm.IdentityPath, - } - response = append(response, &mlr) - } - return response, nil -} - -func (v AppleHVVirtualization) LoadVMByName(name string) (machine.VM, error) { - m := MacMachine{Name: name} - return m.loadFromFile() -} - -func (v AppleHVVirtualization) NewMachine(opts define.InitOptions) (machine.VM, error) { - m := MacMachine{Name: opts.Name} - - if len(opts.USBs) > 0 { - return nil, fmt.Errorf("USB host passthrough is not supported for applehv machines") - } - - configDir, err := machine.GetConfDir(define.AppleHvVirt) - if err != nil { - return nil, err - } - - configPath, err := define.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil) - if err != nil { - return nil, err - } - m.ConfigPath = *configPath - - dataDir, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - return nil, err - } - - if err := ignition.SetIgnitionFile(&m.IgnitionFile, vmtype, m.Name, configDir); err != nil { - return nil, err - } - - // Set creation time - m.Created = time.Now() - - m.ResourceConfig = vmconfigs.ResourceConfig{ - CPUs: opts.CPUS, - DiskSize: opts.DiskSize, - // Diskpath will be needed - Memory: opts.Memory, - } - bl := vfConfig.NewEFIBootloader(fmt.Sprintf("%s/%ss", dataDir, opts.Name), true) - m.Vfkit.VirtualMachine = vfConfig.NewVirtualMachine(uint(opts.CPUS), opts.Memory, bl) - - if err := m.writeConfig(); err != nil { - return nil, err - } - return m.loadFromFile() -} - -func (v AppleHVVirtualization) RemoveAndCleanMachines() error { - // This can be implemented when host networking is completed. - return define.ErrNotImplemented -} - -func (v AppleHVVirtualization) VMType() define.VMType { - return vmtype -} - -func (v AppleHVVirtualization) loadFromLocalJson() ([]*MacMachine, error) { - var ( - jsonFiles []string - mms []*MacMachine - ) - configDir, err := machine.GetConfDir(v.VMType()) - if err != nil { - return nil, err - } - if err := filepath.WalkDir(configDir, func(input string, d fs.DirEntry, e error) error { - if e != nil { - return e - } - if filepath.Ext(d.Name()) == ".json" { - jsonFiles = append(jsonFiles, input) - } - return nil - }); err != nil { - return nil, err - } - - for _, jsonFile := range jsonFiles { - mm, err := loadMacMachineFromJSON(jsonFile) - if err != nil { - return nil, err - } - if err != nil { - return nil, err - } - mms = append(mms, mm) - } - return mms, nil -} diff --git a/pkg/machine/applehv/ignition.go b/pkg/machine/applehv/ignition.go index 678468e62e..0e7581c77e 100644 --- a/pkg/machine/applehv/ignition.go +++ b/pkg/machine/applehv/ignition.go @@ -7,14 +7,20 @@ import ( "net/http" "github.com/containers/podman/v4/pkg/machine/define" + "github.com/containers/podman/v4/pkg/machine/vmconfigs" "github.com/sirupsen/logrus" ) // serveIgnitionOverSock allows podman to open a small httpd instance on the vsock between the host // and guest to inject the ignitionfile into fcos -func (m *MacMachine) serveIgnitionOverSock(ignitionSocket *define.VMFile) error { - logrus.Debugf("reading ignition file: %s", m.IgnitionFile.GetPath()) - ignFile, err := m.IgnitionFile.Read() +func serveIgnitionOverSock(ignitionSocket *define.VMFile, mc *vmconfigs.MachineConfig) error { + ignitionFile, err := mc.IgnitionFile() + if err != nil { + return err + } + + logrus.Debugf("reading ignition file: %s", ignitionFile.GetPath()) + ignFile, err := ignitionFile.Read() if err != nil { return err } @@ -22,7 +28,7 @@ func (m *MacMachine) serveIgnitionOverSock(ignitionSocket *define.VMFile) error mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { _, err := w.Write(ignFile) if err != nil { - logrus.Error("failed to serve ignition file: %v", err) + logrus.Errorf("failed to serve ignition file: %v", err) } }) listener, err := net.Listen("unix", ignitionSocket.GetPath()) diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index 1a13435b07..5eab08ea31 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -3,505 +3,27 @@ package applehv import ( - "context" - "encoding/json" - "errors" "fmt" - "io/fs" - "net" "os" "os/exec" - "path/filepath" - "strconv" - "strings" "syscall" - "time" - "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/strongunits" - 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/connection" "github.com/containers/podman/v4/pkg/machine/define" "github.com/containers/podman/v4/pkg/machine/ignition" - "github.com/containers/podman/v4/pkg/machine/sockets" "github.com/containers/podman/v4/pkg/machine/vmconfigs" "github.com/containers/podman/v4/pkg/systemd/parser" - "github.com/containers/podman/v4/utils" - "github.com/containers/storage/pkg/lockfile" - vfConfig "github.com/crc-org/vfkit/pkg/config" vfRest "github.com/crc-org/vfkit/pkg/rest" - "github.com/docker/go-units" "github.com/sirupsen/logrus" ) -var ( - // vmtype refers to qemu (vs libvirt, krun, etc). - vmtype = define.AppleHvVirt -) +func (a *AppleHVStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error, error) { + mc.Lock() + defer mc.Unlock() -const ( - dockerSock = "/var/run/docker.sock" - dockerConnectTimeout = 5 * time.Second - 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 -} - -type MacMachine struct { - // ConfigPath is the fully qualified path to the configuration file - ConfigPath define.VMFile - // HostUser contains info about host user - vmconfigs.HostUser - // ImageConfig describes the bootable image - machine.ImageConfig - // Mounts is the list of remote filesystems to 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 - vmconfigs.ResourceConfig - // SSHConfig for accessing the remote vm - 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 - Created time.Time - // LastUp contains the last recorded uptime - LastUp time.Time - // The VFKit endpoint where we can interact with the VM - Vfkit vfkit.VfkitHelper - LogPath define.VMFile - GvProxyPid define.VMFile - GvProxySock define.VMFile - - // Used at runtime for serializing write operations - lock *lockfile.LockFile -} - -// setGVProxyInfo sets the VM's gvproxy pid and socket files -func (m *MacMachine) setGVProxyInfo(runtimeDir string) error { - gvProxyPid, err := define.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.pid"), nil) - if err != nil { - return err - } - m.GvProxyPid = *gvProxyPid - - return sockets.SetSocket(&m.GvProxySock, filepath.Join(runtimeDir, "gvproxy.sock"), nil) -} - -// setVfkitInfo stores the default devices, sets the vfkit endpoint, and -// locates/stores the path to the binary -func (m *MacMachine) setVfkitInfo(cfg *config.Config, readySocket define.VMFile) error { - defaultDevices, err := getDefaultDevices(m.ImagePath.GetPath(), m.LogPath.GetPath(), readySocket.GetPath()) - if err != nil { - return err - } - // Store VFKit stuffs - vfkitPath, err := cfg.FindHelperBinary("vfkit", false) - if err != nil { - return err - } - vfkitBinaryPath, err := define.NewMachineFile(vfkitPath, nil) - if err != nil { - return err - } - - m.Vfkit.VirtualMachine.Devices = defaultDevices - randPort, err := utils.GetRandomPort() - if err != nil { - return err - } - - m.Vfkit.Endpoint = localhostURI + ":" + strconv.Itoa(randPort) - m.Vfkit.VfkitBinaryPath = vfkitBinaryPath - - return nil -} - -// addMountsToVM converts the volumes passed through the CLI to virtio-fs mounts -// and adds them to the machine -func (m *MacMachine) addMountsToVM(opts define.InitOptions, virtiofsMnts *[]machine.VirtIoFs) error { - var mounts []vmconfigs.Mount - for _, volume := range opts.Volumes { - source, target, _, readOnly, err := machine.ParseVolumeFromPath(volume) - if err != nil { - return err - } - mnt := machine.NewVirtIoFsMount(source, target, readOnly) - *virtiofsMnts = append(*virtiofsMnts, mnt) - mounts = append(mounts, mnt.ToMount()) - } - m.Mounts = mounts - - return nil -} - -func (m *MacMachine) Init(opts define.InitOptions) (bool, error) { - var ( - key string - virtiofsMnts []machine.VirtIoFs - err error - ) - - // cleanup half-baked files if init fails at any point - callbackFuncs := machine.InitCleanup() - defer callbackFuncs.CleanIfErr(&err) - go callbackFuncs.CleanOnSignal() - - callbackFuncs.Add(m.ConfigPath.Delete) - dataDir, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - return false, err - } - cfg, err := config.Default() - if err != nil { - return false, err - } - - dl, err := VirtualizationProvider().NewDownload(m.Name) - if err != nil { - return false, err - } - - imagePath, strm, err := dl.AcquireVMImage(opts.ImagePath) - if err != nil { - return false, err - } - callbackFuncs.Add(imagePath.Delete) - - // Set the values for imagePath and strm - m.ImagePath = *imagePath - m.ImageStream = strm.String() - - logPath, err := define.NewMachineFile(filepath.Join(dataDir, fmt.Sprintf("%s.log", m.Name)), nil) - if err != nil { - return false, err - } - callbackFuncs.Add(logPath.Delete) - - m.LogPath = *logPath - runtimeDir, err := m.getRuntimeDir() - if err != nil { - return false, err - } - - if err := sockets.SetSocket(&m.ReadySocket, sockets.ReadySocketPath(runtimeDir, m.Name), nil); err != nil { - return false, err - } - - if err = m.setGVProxyInfo(runtimeDir); err != nil { - return false, err - } - - if err = m.setVfkitInfo(cfg, m.ReadySocket); err != nil { - return false, err - } - - m.IdentityPath, err = machine.GetSSHIdentityPath(define.DefaultIdentityName) - if err != nil { - return false, err - } - m.Rootful = opts.Rootful - m.RemoteUsername = opts.Username - - m.UID = os.Getuid() - - sshPort, err := utils.GetRandomPort() - if err != nil { - return false, err - } - m.Port = sshPort - - if err = m.addMountsToVM(opts, &virtiofsMnts); err != nil { - return false, err - } - - err = connection.AddSSHConnectionsToPodmanSocket( - m.UID, - m.Port, - m.IdentityPath, - m.Name, - m.RemoteUsername, - opts, - ) - if err != nil { - return false, err - } - callbackFuncs.Add(m.removeSystemConnections) - - logrus.Debugf("resizing disk to %d GiB", opts.DiskSize) - if err = m.resizeDisk(strongunits.GiB(opts.DiskSize)); err != nil { - return false, err - } - - if err = m.writeConfig(); err != nil { - return false, err - } - - if len(opts.IgnitionPath) < 1 { - key, err = machine.GetSSHKeys(m.IdentityPath) - if err != nil { - return false, err - } - } - - builder := ignition.NewIgnitionBuilder(ignition.DynamicIgnition{ - Name: opts.Username, - Key: key, - VMName: m.Name, - VMType: define.AppleHvVirt, - TimeZone: opts.TimeZone, - WritePath: m.IgnitionFile.GetPath(), - UID: m.UID, - Rootful: m.Rootful, - }) - - if len(opts.IgnitionPath) > 0 { - err = builder.BuildWithIgnitionFile(opts.IgnitionPath) - return false, err - } - - if err := builder.GenerateIgnitionConfig(); err != nil { - return false, err - } - - readyUnitFile, err := ignition.CreateReadyUnitFile(define.AppleHvVirt, nil) - if err != nil { - return false, err - } - - builder.WithUnit(ignition.Unit{ - Enabled: ignition.BoolToPtr(true), - Name: "ready.service", - Contents: ignition.StrToPtr(readyUnitFile), - }) - builder.WithUnit(generateSystemDFilesForVirtiofsMounts(virtiofsMnts)...) - - // TODO Ignition stuff goes here - err = builder.Build() - callbackFuncs.Add(m.IgnitionFile.Delete) - - return err == nil, err -} - -func (m *MacMachine) removeSystemConnections() error { - return connection.RemoveConnections(m.Name, fmt.Sprintf("%s-root", m.Name)) -} - -func (m *MacMachine) Inspect() (*machine.InspectInfo, error) { - vmState, err := m.Vfkit.State() - if err != nil { - return nil, err - } - - podmanSocket, err := m.forwardSocketPath() - if err != nil { - return nil, err - } - - ii := machine.InspectInfo{ - ConfigPath: m.ConfigPath, - ConnectionInfo: machine.ConnectionConfig{ - PodmanSocket: podmanSocket, - PodmanPipe: nil, - }, - Created: m.Created, - Image: machine.ImageConfig{ - IgnitionFile: m.IgnitionFile, - ImageStream: m.ImageStream, - ImagePath: m.ImagePath, - }, - LastUp: m.LastUp, - Name: m.Name, - Resources: vmconfigs.ResourceConfig{ - CPUs: m.CPUs, - DiskSize: m.DiskSize, - Memory: m.Memory, - }, - SSHConfig: m.SSHConfig, - State: vmState, - Rootful: m.Rootful, - } - return &ii, nil -} - -// collectFilesToDestroy retrieves the files that will be destroyed by `Remove` -func (m *MacMachine) collectFilesToDestroy(opts machine.RemoveOptions) []string { - files := []string{} - if !opts.SaveIgnition { - files = append(files, m.IgnitionFile.GetPath()) - } - - if !opts.SaveImage { - files = append(files, m.ImagePath.GetPath()) - } - - files = append(files, m.ConfigPath.GetPath()) - return files -} - -func (m *MacMachine) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) { - var ( - files []string - ) - - m.lock.Lock() - defer m.lock.Unlock() - - vmState, err := m.Vfkit.State() - if err != nil { - return "", nil, err - } - - if vmState == define.Running { - if !opts.Force { - return "", nil, &define.ErrVMRunningCannotDestroyed{Name: m.Name} - } - if err := m.Vfkit.Stop(true, true); err != nil { - return "", nil, err - } - defer func() { - if err := machine.CleanupGVProxy(m.GvProxyPid); err != nil { - logrus.Error(err) - } - }() - } - - files = m.collectFilesToDestroy(opts) - - confirmationMessage := "\nThe following files will be deleted:\n\n" - for _, msg := range files { - confirmationMessage += msg + "\n" - } - - confirmationMessage += "\n" - return confirmationMessage, func() error { - connection.RemoveFilesAndConnections(files, m.Name, m.Name+"-root") - // TODO We will need something like this for applehv too i think - /* - // Remove the HVSOCK for networking - if err := m.NetworkHVSock.Remove(); err != nil { - logrus.Errorf("unable to remove registry entry for %s: %q", m.NetworkHVSock.KeyName, err) - } - - // Remove the HVSOCK for events - if err := m.ReadyHVSock.Remove(); err != nil { - logrus.Errorf("unable to remove registry entry for %s: %q", m.NetworkHVSock.KeyName, err) - } - */ - return nil - }, nil -} - -func (m *MacMachine) writeConfig() error { - b, err := json.MarshalIndent(m, "", " ") - if err != nil { - return err - } - return os.WriteFile(m.ConfigPath.Path, b, 0644) -} - -func (m *MacMachine) setRootful(rootful bool) error { - if err := machine.SetRootful(rootful, m.Name, m.Name+"-root"); err != nil { - return err - } - - m.HostUser.Modified = true - return nil -} - -func (m *MacMachine) Set(name string, opts machine.SetOptions) ([]error, error) { - var setErrors []error - - m.lock.Lock() - defer m.lock.Unlock() - - vmState, err := m.State(false) - if err != nil { - return nil, err - } - if vmState != define.Stopped { - return nil, define.ErrWrongState - } - if cpus := opts.CPUs; cpus != nil { - m.CPUs = *cpus - } - if mem := opts.Memory; mem != nil { - m.Memory = *mem - } - if newSize := opts.DiskSize; newSize != nil { - if *newSize < m.DiskSize { - setErrors = append(setErrors, errors.New("new disk size smaller than existing disk size: cannot shrink disk size")) - } else { - m.DiskSize = *newSize - if err := m.resizeDisk(strongunits.GiB(*opts.DiskSize)); err != nil { - setErrors = append(setErrors, err) - } - } - } - if opts.USBs != nil { - setErrors = append(setErrors, errors.New("changing USBs not supported for applehv machines")) - } - - if opts.Rootful != nil && m.Rootful != *opts.Rootful { - if err := m.setRootful(*opts.Rootful); err != nil { - setErrors = append(setErrors, fmt.Errorf("failed to set rootful option: %w", err)) - } else { - m.Rootful = *opts.Rootful - } - } - - // Write the machine config to the filesystem - err = m.writeConfig() - setErrors = append(setErrors, err) - switch len(setErrors) { - case 0: - return setErrors, nil - case 1: - return nil, setErrors[0] - default: - // Number of errors is 2 or more - lastErr := setErrors[len(setErrors)-1] - return setErrors[:len(setErrors)-1], lastErr - } -} - -func (m *MacMachine) SSH(name string, opts machine.SSHOptions) error { - st, err := m.State(false) - if err != nil { - return err - } - if st != define.Running { - return fmt.Errorf("vm %q is not running", m.Name) - } - username := opts.Username - if username == "" { - username = m.RemoteUsername - } - return machine.CommonSSH(username, m.IdentityPath, m.Name, m.Port, opts.Args) -} - -// deleteIgnitionSocket retrieves the ignition socket, deletes it, and returns a -// pointer to the `VMFile` -func (m *MacMachine) deleteIgnitionSocket() (*define.VMFile, error) { - ignitionSocket, err := m.getIgnitionSock() - if err != nil { - return nil, err - } - if err := ignitionSocket.Delete(); err != nil { - return nil, err - } - return ignitionSocket, nil + // TODO we could delete the vfkit pid/log files if we wanted to be thorough + return []string{}, func() error { return nil }, nil } // getIgnitionVsockDeviceAsCLI retrieves the ignition vsock device and converts @@ -546,424 +68,18 @@ func getVfKitEndpointCMDArgs(endpoint string) ([]string, error) { return restEndpoint.ToCmdLine() } -// addVolumesToVfKit adds the VM's mounts to vfkit's devices -func (m *MacMachine) addVolumesToVfKit() error { - for _, vol := range m.Mounts { - virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, vol.Tag) - if err != nil { - return err - } - m.Vfkit.VirtualMachine.Devices = append(m.Vfkit.VirtualMachine.Devices, virtfsDevice) - } - return nil -} - -func (m *MacMachine) Start(name string, opts machine.StartOptions) error { - var ignitionSocket *define.VMFile - - m.lock.Lock() - defer m.lock.Unlock() - - st, err := m.State(false) - if err != nil { - return err - } - - if st == define.Running { - return define.ErrVMAlreadyRunning - } - - if _, err := m.getRuntimeDir(); err != nil { - return err - } - - // TODO handle returns from startHostNetworking - forwardSock, forwardState, err := m.startHostNetworking() - if err != nil { - return err - } - - // Add networking - netDevice, err := vfConfig.VirtioNetNew("5a:94:ef:e4:0c:ee") - if err != nil { - return err - } - // Set user networking with gvproxy - netDevice.SetUnixSocketPath(m.GvProxySock.GetPath()) - - m.Vfkit.VirtualMachine.Devices = append(m.Vfkit.VirtualMachine.Devices, netDevice) - - if err := m.addVolumesToVfKit(); err != nil { - return err - } - - // To start the VM, we need to call vfkit - - logrus.Debugf("vfkit path is: %s", m.Vfkit.VfkitBinaryPath.Path) - cmd, err := m.Vfkit.VirtualMachine.Cmd(m.Vfkit.VfkitBinaryPath.Path) - if err != nil { - return err - } - - vfkitEndpointArgs, err := getVfKitEndpointCMDArgs(m.Vfkit.Endpoint) - if err != nil { - return err - } - cmd.Args = append(cmd.Args, vfkitEndpointArgs...) - - firstBoot, err := m.isFirstBoot() - if err != nil { - return err - } - - if firstBoot { - // If this is the first boot of the vm, we need to add the vsock - // device to vfkit so we can inject the ignition file - ignitionSocket, err = m.deleteIgnitionSocket() - if err != nil { - return err - } - - ignitionVsockDeviceCLI, err := getIgnitionVsockDeviceAsCLI(ignitionSocket.GetPath()) - if err != nil { - return err - } - cmd.Args = append(cmd.Args, ignitionVsockDeviceCLI...) - } - - if logrus.IsLevelEnabled(logrus.DebugLevel) { - debugDevArgs, err := getDebugDevicesCMDArgs() - if err != nil { - return err - } - cmd.Args = append(cmd.Args, debugDevArgs...) - cmd.Args = append(cmd.Args, "--gui") // add command line switch to pop the gui open - } - - readSocketBaseDir := filepath.Dir(m.ReadySocket.GetPath()) - if err := os.MkdirAll(readSocketBaseDir, 0755); err != nil { - return err - } - - if firstBoot { - logrus.Debug("first boot detected") - logrus.Debugf("serving ignition file over %s", ignitionSocket.GetPath()) - go func() { - if err := m.serveIgnitionOverSock(ignitionSocket); err != nil { - logrus.Error(err) - } - logrus.Debug("ignition vsock server exited") - }() - } - - if err := m.ReadySocket.Delete(); err != nil { - return err - } - - logrus.Debugf("listening for ready on: %s", m.ReadySocket.GetPath()) - readyListen, err := net.Listen("unix", m.ReadySocket.GetPath()) - if err != nil { - return err - } - - logrus.Debug("waiting for ready notification") - readyChan := make(chan error) - go sockets.ListenAndWaitOnSocket(readyChan, readyListen) - - if err := cmd.Start(); err != nil { - return err - } - - processErrChan := make(chan error) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go func() { - defer close(processErrChan) - for { - select { - case <-ctx.Done(): - return - default: - } - if err := checkProcessRunning("vfkit", cmd.Process.Pid); err != nil { - processErrChan <- err - return - } - // lets poll status every half second - time.Sleep(500 * time.Millisecond) - } - }() - - // wait for either socket or to be ready or process to have exited - select { - case err := <-processErrChan: - if err != nil { - return err - } - case err := <-readyChan: - if err != nil { - return err - } - } - - logrus.Debug("ready notification received") - machine.WaitAPIAndPrintInfo( - forwardState, - m.Name, - findClaimHelper(), - forwardSock, - opts.NoInfo, - m.isIncompatible(), - m.Rootful, - ) - - // update the podman/docker socket service if the host user has been modified at all (UID or Rootful) - if m.HostUser.Modified { - if machine.UpdatePodmanDockerSockService(m, name, m.UID, m.Rootful) == nil { - // Reset modification state if there are no errors, otherwise ignore errors - // which are already logged - m.HostUser.Modified = false - _ = m.writeConfig() - } - } - - return nil -} - -func (m *MacMachine) State(_ bool) (define.Status, error) { - vmStatus, err := m.Vfkit.State() +func (a *AppleHVStubber) State(mc *vmconfigs.MachineConfig, _ bool) (define.Status, error) { + vmStatus, err := mc.AppleHypervisor.Vfkit.State() if err != nil { return "", err } return vmStatus, nil } -func (m *MacMachine) Stop(name string, opts machine.StopOptions) error { - m.lock.Lock() - defer m.lock.Unlock() - - vmState, err := m.State(false) - if err != nil { - return err - } - - if vmState != define.Running { - return nil - } - - defer func() { - if err := machine.CleanupGVProxy(m.GvProxyPid); err != nil { - logrus.Error(err) - } - }() - if err := m.Vfkit.Stop(false, true); err != nil { - return err - } - - // keep track of last up - m.LastUp = time.Now() - return m.writeConfig() -} - -// getVMConfigPath is a simple wrapper for getting the fully-qualified -// path of the vm json config file. It should be used to get conformity -func getVMConfigPath(configDir, vmName string) string { - return filepath.Join(configDir, fmt.Sprintf("%s.json", vmName)) -} - -func (m *MacMachine) loadFromFile() (*MacMachine, error) { - if len(m.Name) < 1 { - return nil, errors.New("encountered machine with no name") - } - - jsonPath, err := m.jsonConfigPath() - if err != nil { - return nil, err - } - - mm, err := loadMacMachineFromJSON(jsonPath) - if err != nil { - return nil, err - } - - lock, err := machine.GetLock(mm.Name, vmtype) - if err != nil { - return nil, err - } - mm.lock = lock - - return mm, nil -} - -func loadMacMachineFromJSON(fqConfigPath string) (*MacMachine, error) { - b, err := os.ReadFile(fqConfigPath) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - name := strings.TrimSuffix(filepath.Base(fqConfigPath), ".json") - return nil, fmt.Errorf("%s: %w", name, define.ErrNoSuchVM) - } - return nil, err - } - mm := new(MacMachine) - if err := json.Unmarshal(b, mm); err != nil { - return nil, err - } - return mm, nil -} - -func (m *MacMachine) jsonConfigPath() (string, error) { - configDir, err := machine.GetConfDir(define.AppleHvVirt) - if err != nil { - return "", err - } - return getVMConfigPath(configDir, m.Name), nil -} - -func getVMInfos() ([]*machine.ListResponse, error) { - vmConfigDir, err := machine.GetConfDir(vmtype) - if err != nil { - return nil, err - } - - var listed []*machine.ListResponse - - if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { - vm := new(MacMachine) - if strings.HasSuffix(d.Name(), ".json") { - fullPath := filepath.Join(vmConfigDir, d.Name()) - b, err := os.ReadFile(fullPath) - if err != nil { - return err - } - err = json.Unmarshal(b, vm) - if err != nil { - return err - } - listEntry := new(machine.ListResponse) - - listEntry.Name = vm.Name - listEntry.Stream = vm.ImageStream - listEntry.VMType = define.AppleHvVirt.String() - listEntry.CPUs = vm.CPUs - listEntry.Memory = vm.Memory * units.MiB - listEntry.DiskSize = vm.DiskSize * units.GiB - listEntry.Port = vm.Port - listEntry.RemoteUsername = vm.RemoteUsername - listEntry.IdentityPath = vm.IdentityPath - listEntry.CreatedAt = vm.Created - listEntry.Starting = vm.Starting - - if listEntry.CreatedAt.IsZero() { - listEntry.CreatedAt = time.Now() - vm.Created = time.Now() - if err := vm.writeConfig(); err != nil { - return err - } - } - - vmState, err := vm.State(false) - if err != nil { - return err - } - listEntry.Running = vmState == define.Running - listEntry.LastUp = vm.LastUp - - listed = append(listed, listEntry) - } - return nil - }); err != nil { - return nil, err - } - return listed, err -} - -// setupStartHostNetworkingCmd generates the cmd that will be used to start the -// host networking. Includes the ssh port, gvproxy pid file, gvproxy socket, and -// a debug flag depending on the logrus log level -func (m *MacMachine) setupStartHostNetworkingCmd() (gvproxy.GvproxyCommand, string, machine.APIForwardingState) { - cmd := gvproxy.NewGvproxyCommand() - cmd.SSHPort = m.Port - cmd.PidFile = m.GvProxyPid.GetPath() - cmd.AddVfkitSocket(fmt.Sprintf("unixgram://%s", m.GvProxySock.GetPath())) - cmd.Debug = logrus.IsLevelEnabled(logrus.DebugLevel) - - cmd, forwardSock, state := m.setupAPIForwarding(cmd) - if cmd.Debug { - logrus.Debug(cmd.ToCmdline()) - } - - return cmd, forwardSock, state -} - -func (m *MacMachine) startHostNetworking() (string, machine.APIForwardingState, error) { - var ( - forwardSock string - state machine.APIForwardingState - ) - - // TODO This should probably be added to startHostNetworking everywhere - // GvProxy does not clean up after itself - if err := m.GvProxySock.Delete(); err != nil { - b, err := m.GvProxyPid.Read() - if err != nil { - return "", machine.NoForwarding, err - } - pid, err := strconv.Atoi(string(b)) - if err != nil { - return "", 0, err - } - gvProcess, err := os.FindProcess(pid) - if err != nil { - return "", 0, err - } - // shoot it with a signal 0 and see if it is active - err = gvProcess.Signal(syscall.Signal(0)) - if err == nil { - return "", 0, fmt.Errorf("gvproxy process %s already running", string(b)) - } - if err := m.GvProxySock.Delete(); err != nil { - return "", 0, err - } - } - cfg, err := config.Default() - if err != nil { - return "", machine.NoForwarding, err - } - - gvproxyBinary, err := cfg.FindHelperBinary("gvproxy", false) - if err != nil { - return "", 0, err - } - - logrus.Debugf("gvproxy binary being used: %s", gvproxyBinary) - - cmd, forwardSock, state := m.setupStartHostNetworkingCmd() - c := cmd.Cmd(gvproxyBinary) - if err := c.Start(); err != nil { - return "", 0, fmt.Errorf("unable to execute: %q: %w", cmd.ToCmdline(), err) - } - - // We need to wait and make sure gvproxy is in fact running - // before continuing - for i := 0; i < 10; i++ { - _, err := os.Stat(m.GvProxySock.GetPath()) - if err == nil { - break - } - if err := checkProcessRunning("gvproxy", c.Process.Pid); err != nil { - // gvproxy is no longer running - return "", 0, err - } - logrus.Debugf("gvproxy unixgram socket %q not found: %v", m.GvProxySock.GetPath(), err) - // Sleep for 1/2 second - time.Sleep(500 * time.Millisecond) - } - if err != nil { - // I guess we would also check the pidfile and look to see if it is running - // to? - return "", 0, fmt.Errorf("unable to verify gvproxy is running") - } - return forwardSock, state, nil +func (a *AppleHVStubber) StopVM(mc *vmconfigs.MachineConfig, _ bool) error { + mc.Lock() + defer mc.Unlock() + return mc.AppleHypervisor.Vfkit.Stop(false, true) } // checkProcessRunning checks non blocking if the pid exited @@ -981,95 +97,14 @@ func checkProcessRunning(processName string, pid int) error { return nil } -func (m *MacMachine) setupAPIForwarding(cmd gvproxy.GvproxyCommand) (gvproxy.GvproxyCommand, string, machine.APIForwardingState) { - socket, err := m.forwardSocketPath() - if err != nil { - return cmd, "", machine.NoForwarding - } - - destSock := fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID) - forwardUser := m.RemoteUsername - - if m.Rootful { - destSock = "/run/podman/podman.sock" - forwardUser = "root" - } - - cmd.AddForwardSock(socket.GetPath()) - cmd.AddForwardDest(destSock) - cmd.AddForwardUser(forwardUser) - cmd.AddForwardIdentity(m.IdentityPath) - - link, err := m.userGlobalSocketLink() - if err != nil { - return cmd, socket.GetPath(), machine.MachineLocal - } - - if !dockerClaimSupported() { - return cmd, socket.GetPath(), machine.ClaimUnsupported - } - - if !dockerClaimHelperInstalled() { - return cmd, socket.GetPath(), machine.NotInstalled - } - - if !alreadyLinked(socket.GetPath(), link) { - if checkSockInUse(link) { - return cmd, socket.GetPath(), machine.MachineLocal - } - - _ = os.Remove(link) - if err = os.Symlink(socket.GetPath(), link); err != nil { - logrus.Warnf("could not create user global API forwarding link: %s", err.Error()) - return cmd, socket.GetPath(), machine.MachineLocal - } - } - - if !alreadyLinked(link, dockerSock) { - if checkSockInUse(dockerSock) { - return cmd, socket.GetPath(), machine.MachineLocal - } - - if !claimDockerSock() { - logrus.Warn("podman helper is installed, but was not able to claim the global docker sock") - return cmd, socket.GetPath(), machine.MachineLocal - } - } - - return cmd, socket.GetPath(), machine.MachineLocal - -} - -func (m *MacMachine) dockerSock() (string, error) { - dd, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - return "", err - } - return filepath.Join(dd, "podman.sock"), nil -} - -func (m *MacMachine) forwardSocketPath() (*define.VMFile, error) { - sockName := "podman.sock" - path, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - return nil, fmt.Errorf("Resolving data dir: %s", err.Error()) - } - return define.NewMachineFile(filepath.Join(path, sockName), &sockName) -} - // resizeDisk uses os truncate to resize (only larger) a raw disk. the input size // is assumed GiB -func (m *MacMachine) resizeDisk(newSize strongunits.GiB) error { - if uint64(newSize) < m.DiskSize { - // TODO this error needs to be changed to the common error. would do now but the PR for the common - // error has not merged - return fmt.Errorf("invalid disk size %d: new disk must be larger than %dGB", newSize, m.DiskSize) - } - logrus.Debugf("resizing %s to %d bytes", m.ImagePath.GetPath(), newSize.ToBytes()) +func resizeDisk(mc *vmconfigs.MachineConfig, newSize strongunits.GiB) error { + logrus.Debugf("resizing %s to %d bytes", mc.ImagePath.GetPath(), newSize.ToBytes()) // seems like os.truncate() is not very performant with really large files // so exec'ing out to the command truncate size := fmt.Sprintf("%dG", newSize) - c := exec.Command("truncate", "-s", size, m.ImagePath.GetPath()) + c := exec.Command("truncate", "-s", size, mc.ImagePath.GetPath()) if logrus.IsLevelEnabled(logrus.DebugLevel) { c.Stderr = os.Stderr c.Stdout = os.Stdout @@ -1077,56 +112,6 @@ func (m *MacMachine) resizeDisk(newSize strongunits.GiB) error { return c.Run() } -// isFirstBoot returns a bool reflecting if the machine has been booted before -func (m *MacMachine) isFirstBoot() (bool, error) { - never, err := time.Parse(time.RFC3339, "0001-01-01T00:00:00Z") - if err != nil { - return false, err - } - return m.LastUp == never, nil -} - -func (m *MacMachine) getIgnitionSock() (*define.VMFile, error) { - dataDir, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - return nil, err - } - if err := os.MkdirAll(dataDir, 0755); err != nil { - if !errors.Is(err, os.ErrExist) { - return nil, err - } - } - return define.NewMachineFile(filepath.Join(dataDir, ignitionSocketName), nil) -} - -func (m *MacMachine) getRuntimeDir() (string, error) { - tmpDir, ok := os.LookupEnv("TMPDIR") - if !ok { - tmpDir = "/tmp" - } - rtd := filepath.Join(tmpDir, "podman") - logrus.Debugf("creating runtimeDir: %s", rtd) - if err := os.MkdirAll(rtd, 0755); err != nil { - return "", err - } - - return rtd, nil -} - -func (m *MacMachine) userGlobalSocketLink() (string, error) { - path, err := machine.GetDataDir(define.AppleHvVirt) - if err != nil { - logrus.Errorf("Resolving data dir: %s", err.Error()) - return "", err - } - // User global socket is located in parent directory of machine dirs (one per user) - return filepath.Join(filepath.Dir(path), "podman.sock"), err -} - -func (m *MacMachine) isIncompatible() bool { - return m.UID == -1 -} - func generateSystemDFilesForVirtiofsMounts(mounts []machine.VirtIoFs) []ignition.Unit { // mounting in fcos with virtiofs is a bit of a dance. we need a unit file for the mount, a unit file // for automatic mounting on boot, and a "preparatory" service file that disables FCOS security, performs diff --git a/pkg/machine/applehv/stubber.go b/pkg/machine/applehv/stubber.go new file mode 100644 index 0000000000..062137942d --- /dev/null +++ b/pkg/machine/applehv/stubber.go @@ -0,0 +1,311 @@ +//go:build darwin + +package applehv + +import ( + "context" + "fmt" + "net" + "strconv" + "time" + + "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/ignition" + "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/utils" + vfConfig "github.com/crc-org/vfkit/pkg/config" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +// applehcMACAddress is a pre-defined mac address that vfkit recognizes +// and is required for network flow +const applehvMACAddress = "5a:94:ef:e4:0c:ee" + +var ( + vfkitCommand = "vfkit" + gvProxyWaitBackoff = 500 * time.Millisecond + gvProxyMaxBackoffAttempts = 6 +) + +type AppleHVStubber struct { + vmconfigs.AppleHVConfig +} + +func (a AppleHVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, ignBuilder *ignition.IgnitionBuilder) error { + mc.AppleHypervisor = new(vmconfigs.AppleHVConfig) + mc.AppleHypervisor.Vfkit = vfkit.VfkitHelper{} + bl := vfConfig.NewEFIBootloader(fmt.Sprintf("%s/efi-bl-%s", opts.Dirs.DataDir.GetPath(), opts.Name), true) + mc.AppleHypervisor.Vfkit.VirtualMachine = vfConfig.NewVirtualMachine(uint(mc.Resources.CPUs), mc.Resources.Memory, bl) + + randPort, err := utils.GetRandomPort() + if err != nil { + return err + } + mc.AppleHypervisor.Vfkit.Endpoint = localhostURI + ":" + strconv.Itoa(randPort) + + var virtiofsMounts []machine.VirtIoFs + for _, mnt := range mc.Mounts { + virtiofsMounts = append(virtiofsMounts, machine.MountToVirtIOFs(mnt)) + } + + // Populate the ignition file with virtiofs stuff + ignBuilder.WithUnit(generateSystemDFilesForVirtiofsMounts(virtiofsMounts)...) + + return resizeDisk(mc, strongunits.GiB(mc.Resources.DiskSize)) +} + +func (a AppleHVStubber) GetHyperVisorVMs() ([]string, error) { + // not applicable for applehv + return nil, nil +} + +func (a AppleHVStubber) MountType() vmconfigs.VolumeMountType { + return vmconfigs.VirtIOFS +} + +func (a AppleHVStubber) MountVolumesToVM(_ *vmconfigs.MachineConfig, _ bool) error { + // virtiofs: nothing to do here + return nil +} + +func (a AppleHVStubber) RemoveAndCleanMachines(_ *define.MachineDirs) error { + return nil +} + +func (a AppleHVStubber) SetProviderAttrs(mc *vmconfigs.MachineConfig, cpus, memory *uint64, newDiskSize *strongunits.GiB) error { + if newDiskSize != nil { + if err := resizeDisk(mc, *newDiskSize); err != nil { + return err + } + } + // VFKit does not require saving memory, disk, or cpu + return nil +} + +func (a AppleHVStubber) StartNetworking(mc *vmconfigs.MachineConfig, cmd *gvproxy.GvproxyCommand) error { + gvProxySock, err := mc.GVProxySocket() + if err != nil { + return err + } + // make sure it does not exist before gvproxy is called + if err := gvProxySock.Delete(); err != nil { + logrus.Error(err) + } + cmd.AddVfkitSocket(fmt.Sprintf("unixgram://%s", gvProxySock.GetPath())) + return nil +} + +func (a AppleHVStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) { + var ( + ignitionSocket *define.VMFile + ) + + if bl := mc.AppleHypervisor.Vfkit.VirtualMachine.Bootloader; bl == nil { + return nil, nil, fmt.Errorf("unable to determine boot loader for this machine") + } + + // Add networking + netDevice, err := vfConfig.VirtioNetNew(applehvMACAddress) + if err != nil { + return nil, nil, err + } + // Set user networking with gvproxy + + gvproxySocket, err := mc.GVProxySocket() + if err != nil { + return nil, nil, err + } + + // Wait on gvproxy to be running and aware + if err := waitForGvProxy(gvproxySocket); err != nil { + return nil, nil, err + } + + netDevice.SetUnixSocketPath(gvproxySocket.GetPath()) + + readySocket, err := mc.ReadySocket() + if err != nil { + return nil, nil, err + } + + logfile, err := mc.LogFile() + if err != nil { + return nil, nil, err + } + + // create a one-time virtual machine for starting because we dont want all this information in the + // machineconfig if possible. the preference was to derive this stuff + vm := vfConfig.NewVirtualMachine(uint(mc.Resources.CPUs), mc.Resources.Memory, mc.AppleHypervisor.Vfkit.VirtualMachine.Bootloader) + + defaultDevices, err := getDefaultDevices(mc.ImagePath.GetPath(), logfile.GetPath(), readySocket.GetPath()) + if err != nil { + return nil, nil, err + } + + vm.Devices = append(vm.Devices, defaultDevices...) + vm.Devices = append(vm.Devices, netDevice) + + mounts, err := virtIOFsToVFKitVirtIODevice(mc.Mounts) + if err != nil { + return nil, nil, err + } + vm.Devices = append(vm.Devices, mounts...) + + // To start the VM, we need to call vfkit + cfg, err := config.Default() + if err != nil { + return nil, nil, err + } + + vfkitBinaryPath, err := cfg.FindHelperBinary(vfkitCommand, true) + if err != nil { + return nil, nil, err + } + + logrus.Debugf("vfkit path is: %s", vfkitBinaryPath) + + cmd, err := vm.Cmd(vfkitBinaryPath) + if err != nil { + return nil, nil, err + } + + vfkitEndpointArgs, err := getVfKitEndpointCMDArgs(mc.AppleHypervisor.Vfkit.Endpoint) + if err != nil { + return nil, nil, err + } + + machineDataDir, err := mc.DataDir() + if err != nil { + return nil, nil, err + } + + cmd.Args = append(cmd.Args, vfkitEndpointArgs...) + + firstBoot, err := mc.IsFirstBoot() + if err != nil { + return nil, nil, err + } + + if logrus.IsLevelEnabled(logrus.DebugLevel) { + debugDevArgs, err := getDebugDevicesCMDArgs() + if err != nil { + return nil, nil, err + } + cmd.Args = append(cmd.Args, debugDevArgs...) + cmd.Args = append(cmd.Args, "--gui") // add command line switch to pop the gui open + } + + if firstBoot { + // If this is the first boot of the vm, we need to add the vsock + // device to vfkit so we can inject the ignition file + socketName := fmt.Sprintf("%s-%s", mc.Name, ignitionSocketName) + ignitionSocket, err = machineDataDir.AppendToNewVMFile(socketName, &socketName) + if err != nil { + return nil, nil, err + } + if err := ignitionSocket.Delete(); err != nil { + logrus.Errorf("unable to delete ignition socket: %q", err) + } + + ignitionVsockDeviceCLI, err := getIgnitionVsockDeviceAsCLI(ignitionSocket.GetPath()) + if err != nil { + return nil, nil, err + } + cmd.Args = append(cmd.Args, ignitionVsockDeviceCLI...) + + logrus.Debug("first boot detected") + logrus.Debugf("serving ignition file over %s", ignitionSocket.GetPath()) + go func() { + if err := serveIgnitionOverSock(ignitionSocket, mc); err != nil { + logrus.Error(err) + } + logrus.Debug("ignition vsock server exited") + }() + } + + logrus.Debugf("listening for ready on: %s", readySocket.GetPath()) + if err := readySocket.Delete(); err != nil { + logrus.Warnf("unable to delete previous ready socket: %q", err) + } + readyListen, err := net.Listen("unix", readySocket.GetPath()) + if err != nil { + return nil, nil, err + } + + logrus.Debug("waiting for ready notification") + readyChan := make(chan error) + go sockets.ListenAndWaitOnSocket(readyChan, readyListen) + + logrus.Debugf("vfkit command-line: %v", cmd.Args) + + if err := cmd.Start(); err != nil { + return nil, nil, err + } + + returnFunc := func() error { + processErrChan := make(chan error) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + defer close(processErrChan) + for { + select { + case <-ctx.Done(): + return + default: + } + if err := checkProcessRunning("vfkit", cmd.Process.Pid); err != nil { + processErrChan <- err + return + } + // lets poll status every half second + time.Sleep(500 * time.Millisecond) + } + }() + + // wait for either socket or to be ready or process to have exited + select { + case err := <-processErrChan: + if err != nil { + return err + } + case err := <-readyChan: + if err != nil { + return err + } + logrus.Debug("ready notification received") + } + return nil + } + return cmd.Process.Release, returnFunc, nil +} + +func (a AppleHVStubber) StopHostNetworking() error { + // TODO implement me + panic("implement me") +} + +func (a AppleHVStubber) VMType() define.VMType { + return define.AppleHvVirt +} + +func waitForGvProxy(gvproxySocket *define.VMFile) error { + backoffWait := gvProxyWaitBackoff + logrus.Debug("checking that gvproxy is running") + for i := 0; i < gvProxyMaxBackoffAttempts; i++ { + err := unix.Access(gvproxySocket.GetPath(), unix.W_OK) + if err == nil { + return nil + } + time.Sleep(backoffWait) + backoffWait *= 2 + } + return fmt.Errorf("unable to connect to gvproxy %q", gvproxySocket.GetPath()) +} diff --git a/pkg/machine/applehv/vfkit.go b/pkg/machine/applehv/vfkit.go index 7014d3555b..1c38b1eeb4 100644 --- a/pkg/machine/applehv/vfkit.go +++ b/pkg/machine/applehv/vfkit.go @@ -3,10 +3,11 @@ package applehv import ( - "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/pkg/machine/vmconfigs" vfConfig "github.com/crc-org/vfkit/pkg/config" ) +// TODO this signature could be an machineconfig func getDefaultDevices(imagePath, logPath, readyPath string) ([]vfConfig.VirtioDevice, error) { var devices []vfConfig.VirtioDevice @@ -53,11 +54,14 @@ func getIgnitionVsockDevice(path string) (vfConfig.VirtioDevice, error) { return vfConfig.VirtioVsockNew(1024, path, true) } -func VirtIOFsToVFKitVirtIODevice(fs machine.VirtIoFs) vfConfig.VirtioFs { - return vfConfig.VirtioFs{ - DirectorySharingConfig: vfConfig.DirectorySharingConfig{ - MountTag: fs.Tag, - }, - SharedDir: fs.Source, +func virtIOFsToVFKitVirtIODevice(mounts []vmconfigs.Mount) ([]vfConfig.VirtioDevice, error) { + var virtioDevices []vfConfig.VirtioDevice + for _, vol := range mounts { + virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, vol.Tag) + if err != nil { + return nil, err + } + virtioDevices = append(virtioDevices, virtfsDevice) } + return virtioDevices, nil } diff --git a/pkg/machine/applehv/vfkit/config.go b/pkg/machine/applehv/vfkit/config.go index 80be55c864..ca24bfcd90 100644 --- a/pkg/machine/applehv/vfkit/config.go +++ b/pkg/machine/applehv/vfkit/config.go @@ -57,6 +57,9 @@ func (vf *VfkitHelper) getRawState() (define.Status, error) { if err != nil { return "", err } + if err := serverResponse.Body.Close(); err != nil { + logrus.Error(err) + } return ToMachineStatus(response.State) } @@ -66,7 +69,7 @@ func (vf *VfkitHelper) getRawState() (define.Status, error) { func (vf *VfkitHelper) State() (define.Status, error) { vmState, err := vf.getRawState() if err == nil { - return vmState, err + return vmState, nil } if errors.Is(err, unix.ECONNREFUSED) { return define.Stopped, nil @@ -107,7 +110,7 @@ func (vf *VfkitHelper) Stop(force, wait bool) error { waitErr = nil break } - waitDuration = waitDuration * 2 + waitDuration *= 2 logrus.Debugf("backoff wait time: %s", waitDuration.String()) time.Sleep(waitDuration) } diff --git a/pkg/machine/define/vmfile.go b/pkg/machine/define/vmfile.go index 16516e956b..1795a4dc5a 100644 --- a/pkg/machine/define/vmfile.go +++ b/pkg/machine/define/vmfile.go @@ -73,6 +73,7 @@ func NewMachineFile(path string, symlink *string) (*VMFile, error) { return nil, errors.New("invalid symlink path") } mf := VMFile{Path: path} + logrus.Debugf("socket length for %s is %d", path, len(path)) if symlink != nil && len(path) > MaxSocketPathLength { if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) { return nil, err @@ -100,5 +101,5 @@ func (m *VMFile) makeSymlink(symlink *string) error { // AppendToNewVMFile takes a given path and appends it to the existing vmfile path. The new // VMFile is returned func (m *VMFile) AppendToNewVMFile(additionalPath string, symlink *string) (*VMFile, error) { - return NewMachineFile(filepath.Join(m.GetPath(), additionalPath), symlink) + return NewMachineFile(filepath.Join(m.Path, additionalPath), symlink) } diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go index 3da976811a..789eb43e9c 100644 --- a/pkg/machine/e2e/machine_test.go +++ b/pkg/machine/e2e/machine_test.go @@ -59,7 +59,7 @@ var _ = BeforeSuite(func() { downloadLocation := os.Getenv("MACHINE_IMAGE") if downloadLocation == "" { - downloadLocation, err = GetDownload() + downloadLocation, err = GetDownload(testProvider.VMType()) if err != nil { Fail("unable to derive download disk from fedora coreos") } @@ -69,9 +69,15 @@ var _ = BeforeSuite(func() { Fail("machine tests require a file reference to a disk image right now") } - // TODO Fix or remove - this only works for qemu rn - // compressionExtension := fmt.Sprintf(".%s", testProvider.Compression().String()) - compressionExtension := ".xz" + var compressionExtension string + switch testProvider.VMType() { + case define.AppleHvVirt: + compressionExtension = ".gz" + case define.HyperVVirt: + compressionExtension = ".zip" + default: + compressionExtension = ".xz" + } suiteImageName = strings.TrimSuffix(path.Base(downloadLocation), compressionExtension) fqImageName = filepath.Join(tmpDir, suiteImageName) diff --git a/pkg/machine/e2e/pull_test.go b/pkg/machine/e2e/pull_test.go index 80b62b6f4b..b590def06e 100644 --- a/pkg/machine/e2e/pull_test.go +++ b/pkg/machine/e2e/pull_test.go @@ -7,14 +7,16 @@ import ( "net/http" "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/pkg/machine/define" "github.com/coreos/stream-metadata-go/fedoracoreos" "github.com/coreos/stream-metadata-go/stream" "github.com/sirupsen/logrus" ) -func GetDownload() (string, error) { +func GetDownload(vmType define.VMType) (string, error) { var ( - fcosstable stream.Stream + fcosstable stream.Stream + artifactType, format string ) url := fedoracoreos.GetStreamURL("testing") resp, err := http.Get(url.String()) @@ -34,6 +36,19 @@ func GetDownload() (string, error) { if err := json.Unmarshal(body, &fcosstable); err != nil { return "", err } + + switch vmType { + case define.AppleHvVirt: + artifactType = "applehv" + format = "raw.gz" + case define.HyperVVirt: + artifactType = "hyperv" + format = "vhdx.zip" + default: + artifactType = "qemu" + format = "qcow2.xz" + } + arch, ok := fcosstable.Architectures[machine.GetFcosArch()] if !ok { return "", fmt.Errorf("unable to pull VM image: no targetArch in stream") @@ -42,17 +57,17 @@ func GetDownload() (string, error) { if upstreamArtifacts == nil { return "", fmt.Errorf("unable to pull VM image: no artifact in stream") } - upstreamArtifact, ok := upstreamArtifacts["qemu"] + upstreamArtifact, ok := upstreamArtifacts[artifactType] if !ok { - return "", fmt.Errorf("unable to pull VM image: no %s artifact in stream", "qemu") + return "", fmt.Errorf("unable to pull VM image: no %s artifact in stream", artifactType) } formats := upstreamArtifact.Formats if formats == nil { return "", fmt.Errorf("unable to pull VM image: no formats in stream") } - formatType, ok := formats["qcow2.xz"] + formatType, ok := formats[format] if !ok { - return "", fmt.Errorf("unable to pull VM image: no %s format in stream", "qcow2.xz") + return "", fmt.Errorf("unable to pull VM image: no %s format in stream", format) } disk := formatType.Disk return disk.Location, nil diff --git a/pkg/machine/provider/platform_darwin.go b/pkg/machine/provider/platform_darwin.go index ffe8dc2377..e7934f0a88 100644 --- a/pkg/machine/provider/platform_darwin.go +++ b/pkg/machine/provider/platform_darwin.go @@ -5,14 +5,13 @@ import ( "os" "github.com/containers/common/pkg/config" - "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/applehv" "github.com/containers/podman/v4/pkg/machine/define" - "github.com/containers/podman/v4/pkg/machine/qemu" + "github.com/containers/podman/v4/pkg/machine/vmconfigs" "github.com/sirupsen/logrus" ) -func Get() (machine.VirtProvider, error) { +func Get() (vmconfigs.VMProvider, error) { cfg, err := config.Default() if err != nil { return nil, err @@ -28,10 +27,8 @@ func Get() (machine.VirtProvider, error) { logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String()) switch resolvedVMType { - case define.QemuVirt: - return qemu.VirtualizationProvider(), nil case define.AppleHvVirt: - return applehv.VirtualizationProvider(), nil + return new(applehv.AppleHVStubber), nil default: return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) } diff --git a/pkg/machine/qemu/stubber.go b/pkg/machine/qemu/stubber.go index 4d10d3fd76..06f3b19286 100644 --- a/pkg/machine/qemu/stubber.go +++ b/pkg/machine/qemu/stubber.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "github.com/containers/podman/v4/pkg/machine/ignition" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/strongunits" gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types" @@ -68,7 +70,7 @@ func (q *QEMUStubber) setQEMUCommandLine(mc *vmconfigs.MachineConfig) error { return nil } -func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig) error { +func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, _ *ignition.IgnitionBuilder) error { monitor, err := command.NewQMPMonitor(opts.Name, opts.Dirs.RuntimeDir) if err != nil { return err diff --git a/pkg/machine/shim/claim_unsupported.go b/pkg/machine/shim/claim_unsupported.go index 3e3ce835db..0fc9403002 100644 --- a/pkg/machine/shim/claim_unsupported.go +++ b/pkg/machine/shim/claim_unsupported.go @@ -1,4 +1,4 @@ -//build: !darwin +//go:build !darwin package shim diff --git a/pkg/machine/shim/host.go b/pkg/machine/shim/host.go index 1fe1759709..0add3644db 100644 --- a/pkg/machine/shim/host.go +++ b/pkg/machine/shim/host.go @@ -182,7 +182,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M return nil, err } - readyUnitFile, err := ignition.CreateReadyUnitFile(machineDefine.QemuVirt, nil) + readyUnitFile, err := ignition.CreateReadyUnitFile(mp.VMType(), nil) if err != nil { return nil, err } @@ -194,12 +194,8 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M } ignBuilder.WithUnit(readyUnit) - if err := ignBuilder.Build(); err != nil { - return nil, err - } - // Mounts - mc.Mounts = vmconfigs.CmdLineVolumesToMounts(opts.Volumes, mp.MountType()) + mc.Mounts = CmdLineVolumesToMounts(opts.Volumes, mp.MountType()) // TODO AddSSHConnectionToPodmanSocket could take an machineconfig instead if err := connection.AddSSHConnectionsToPodmanSocket(mc.HostUser.UID, mc.SSH.Port, mc.SSH.IdentityPath, mc.Name, mc.SSH.RemoteUsername, opts); err != nil { @@ -211,7 +207,11 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M } callbackFuncs.Add(cleanup) - if err := mp.CreateVM(createOpts, mc); err != nil { + if err := mp.CreateVM(createOpts, mc, &ignBuilder); err != nil { + return nil, err + } + + if err := ignBuilder.Build(); err != nil { return nil, err } @@ -327,7 +327,6 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDe if err != nil { return err } - // if there are generic things that need to be done, a preStart function could be added here // should it be extensive diff --git a/pkg/machine/shim/networking.go b/pkg/machine/shim/networking.go index bae3d23cd1..21277936ad 100644 --- a/pkg/machine/shim/networking.go +++ b/pkg/machine/shim/networking.go @@ -6,6 +6,7 @@ import ( "net" "os" "path/filepath" + "strings" "time" "github.com/containers/common/pkg/config" @@ -101,6 +102,8 @@ func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider) } c := cmd.Cmd(binary) + + logrus.Debugf("gvproxy command-line: %s %s", binary, strings.Join(cmd.ToCmdline(), " ")) if err := c.Start(); err != nil { return forwardSock, 0, fmt.Errorf("unable to execute: %q: %w", cmd.ToCmdline(), err) } diff --git a/pkg/machine/shim/volume.go b/pkg/machine/shim/volume.go new file mode 100644 index 0000000000..fe570b4342 --- /dev/null +++ b/pkg/machine/shim/volume.go @@ -0,0 +1,30 @@ +package shim + +import ( + "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/pkg/machine/vmconfigs" +) + +func CmdLineVolumesToMounts(volumes []string, volumeType vmconfigs.VolumeMountType) []vmconfigs.Mount { + mounts := []vmconfigs.Mount{} + for i, volume := range volumes { + var mount vmconfigs.Mount + tag, source, target, readOnly, _ := vmconfigs.SplitVolume(i, volume) + switch volumeType { + case vmconfigs.VirtIOFS: + virtioMount := machine.NewVirtIoFsMount(source, target, readOnly) + mount = virtioMount.ToMount() + default: + mount = vmconfigs.Mount{ + Type: volumeType.String(), + Tag: tag, + Source: source, + Target: target, + ReadOnly: readOnly, + OriginalInput: volume, + } + } + mounts = append(mounts, mount) + } + return mounts +} diff --git a/pkg/machine/sockets/sockets.go b/pkg/machine/sockets/sockets.go index 6d966dbfb3..1c0c56698f 100644 --- a/pkg/machine/sockets/sockets.go +++ b/pkg/machine/sockets/sockets.go @@ -9,6 +9,7 @@ import ( "time" "github.com/containers/podman/v4/pkg/machine/define" + "github.com/sirupsen/logrus" ) // SetSocket creates a new machine file for the socket and assigns it to @@ -33,10 +34,12 @@ func ReadySocketPath(runtimeDir, machineName string) string { func ListenAndWaitOnSocket(errChan chan<- error, listener net.Listener) { conn, err := listener.Accept() if err != nil { + logrus.Debug("failed to connect to ready socket") errChan <- err return } _, err = bufio.NewReader(conn).ReadString('\n') + logrus.Debug("ready ack received") if closeErr := conn.Close(); closeErr != nil { errChan <- closeErr diff --git a/pkg/machine/vmconfigs/config.go b/pkg/machine/vmconfigs/config.go index aec81d07a9..890db29c78 100644 --- a/pkg/machine/vmconfigs/config.go +++ b/pkg/machine/vmconfigs/config.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/strongunits" gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types" "github.com/containers/podman/v4/pkg/machine/define" + "github.com/containers/podman/v4/pkg/machine/ignition" "github.com/containers/podman/v4/pkg/machine/qemu/command" "github.com/containers/storage/pkg/lockfile" ) @@ -106,7 +107,7 @@ func (f fcosMachineImage) path() string { } type VMProvider interface { //nolint:interfacebloat - CreateVM(opts define.CreateVMOpts, mc *MachineConfig) error + CreateVM(opts define.CreateVMOpts, mc *MachineConfig, builder *ignition.IgnitionBuilder) error GetHyperVisorVMs() ([]string, error) MountType() VolumeMountType MountVolumesToVM(mc *MachineConfig, quiet bool) error diff --git a/pkg/machine/vmconfigs/machine.go b/pkg/machine/vmconfigs/machine.go index c5fb05ce48..0b1575e571 100644 --- a/pkg/machine/vmconfigs/machine.go +++ b/pkg/machine/vmconfigs/machine.go @@ -10,14 +10,12 @@ import ( "strings" "time" - "github.com/containers/podman/v4/pkg/machine/connection" - - "github.com/sirupsen/logrus" - define2 "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/machine/connection" "github.com/containers/podman/v4/pkg/machine/define" "github.com/containers/podman/v4/pkg/machine/lock" "github.com/containers/podman/v4/utils" + "github.com/sirupsen/logrus" ) /* @@ -235,7 +233,15 @@ func (mc *MachineConfig) ReadySocket() (*define.VMFile, error) { if err != nil { return nil, err } - return rtDir.AppendToNewVMFile(mc.Name+".sock", nil) + return readySocket(mc.Name, rtDir) +} + +func (mc *MachineConfig) GVProxySocket() (*define.VMFile, error) { + machineRuntimeDir, err := mc.RuntimeDir() + if err != nil { + return nil, err + } + return gvProxySocket(mc.Name, machineRuntimeDir) } func (mc *MachineConfig) LogFile() (*define.VMFile, error) { @@ -264,6 +270,14 @@ func (mc *MachineConfig) Kind() (define.VMType, error) { return define.UnknownVirt, nil } +func (mc *MachineConfig) IsFirstBoot() (bool, error) { + never, err := time.Parse(time.RFC3339, "0001-01-01T00:00:00Z") + if err != nil { + return false, err + } + return mc.LastUp == never, nil +} + // LoadMachineByName returns a machine config based on the vm name and provider func LoadMachineByName(name string, dirs *define.MachineDirs) (*MachineConfig, error) { fullPath, err := dirs.ConfigDir.AppendToNewVMFile(name+".json", nil) diff --git a/pkg/machine/vmconfigs/sockets.go b/pkg/machine/vmconfigs/sockets.go new file mode 100644 index 0000000000..b33bb34ae8 --- /dev/null +++ b/pkg/machine/vmconfigs/sockets.go @@ -0,0 +1,17 @@ +//go:build !darwin + +package vmconfigs + +import ( + "fmt" + + "github.com/containers/podman/v4/pkg/machine/define" +) + +func gvProxySocket(name string, machineRuntimeDir *define.VMFile) (*define.VMFile, error) { + return machineRuntimeDir.AppendToNewVMFile(fmt.Sprintf("%s-gvproxy.sock", name), nil) +} + +func readySocket(name string, machineRuntimeDir *define.VMFile) (*define.VMFile, error) { + return machineRuntimeDir.AppendToNewVMFile(name+".sock", nil) +} diff --git a/pkg/machine/vmconfigs/sockets_darwin.go b/pkg/machine/vmconfigs/sockets_darwin.go new file mode 100644 index 0000000000..83e4cceec7 --- /dev/null +++ b/pkg/machine/vmconfigs/sockets_darwin.go @@ -0,0 +1,17 @@ +package vmconfigs + +import ( + "fmt" + + "github.com/containers/podman/v4/pkg/machine/define" +) + +func gvProxySocket(name string, machineRuntimeDir *define.VMFile) (*define.VMFile, error) { + socketName := fmt.Sprintf("%s-gvproxy.sock", name) + return machineRuntimeDir.AppendToNewVMFile(socketName, &socketName) +} + +func readySocket(name string, machineRuntimeDir *define.VMFile) (*define.VMFile, error) { + socketName := name + ".sock" + return machineRuntimeDir.AppendToNewVMFile(socketName, &socketName) +} diff --git a/pkg/machine/vmconfigs/volumes.go b/pkg/machine/vmconfigs/volumes.go index edfc3abb9d..8ee2093fd1 100644 --- a/pkg/machine/vmconfigs/volumes.go +++ b/pkg/machine/vmconfigs/volumes.go @@ -58,20 +58,3 @@ func SplitVolume(idx int, volume string) (string, string, string, bool, string) readonly, securityModel := extractMountOptions(paths) return tag, source, target, readonly, securityModel } - -func CmdLineVolumesToMounts(volumes []string, volumeType VolumeMountType) []Mount { - mounts := []Mount{} - for i, volume := range volumes { - tag, source, target, readOnly, _ := SplitVolume(i, volume) - mount := Mount{ - Type: volumeType.String(), - Tag: tag, - Source: source, - Target: target, - ReadOnly: readOnly, - OriginalInput: volume, - } - mounts = append(mounts, mount) - } - return mounts -} diff --git a/pkg/machine/volumes.go b/pkg/machine/volumes.go index b2a9d9de41..a2e71f3d5c 100644 --- a/pkg/machine/volumes.go +++ b/pkg/machine/volumes.go @@ -61,3 +61,13 @@ func NewVirtIoFsMount(src, target string, readOnly bool) VirtIoFs { vfs.Tag = vfs.unitName() return vfs } + +func MountToVirtIOFs(mnt vmconfigs.Mount) VirtIoFs { + return VirtIoFs{ + VolumeKind: VirtIOFsVk, + ReadOnly: mnt.ReadOnly, + Source: mnt.Source, + Tag: mnt.Tag, + Target: mnt.Target, + } +}