mirror of
https://github.com/containers/podman.git
synced 2025-06-23 02:18:13 +08:00
Podman machine AppleHV pass number 3
* Enabled user-mode networking with gvproxy * VirtIOFS volumes supported Signed-off-by: Brent Baude <bbaude@redhat.com> [NO NEW TESTS NEEDED] Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
pkg/machine
84
pkg/machine/applehv/claim.go
Normal file
84
pkg/machine/applehv/claim.go
Normal file
@ -0,0 +1,84 @@
|
||||
//go:build darwin && arm64
|
||||
// +build darwin,arm64
|
||||
|
||||
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
|
||||
}
|
@ -16,6 +16,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
@ -34,6 +35,12 @@ var (
|
||||
vmtype = machine.AppleHvVirt
|
||||
)
|
||||
|
||||
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
|
||||
@ -53,12 +60,6 @@ type MacMachine struct {
|
||||
Mounts []machine.Mount
|
||||
// Name of VM
|
||||
Name string
|
||||
// TODO We will need something like this for applehv but until host networking
|
||||
// is worked out, we cannot be sure what it looks like.
|
||||
/*
|
||||
// NetworkVSock is for the user networking
|
||||
NetworkHVSock machine.HVSockRegistryEntry
|
||||
*/
|
||||
// ReadySocket tells host when vm is booted
|
||||
ReadySocket machine.VMFile
|
||||
// ResourceConfig is physical attrs of the VM
|
||||
@ -72,13 +73,17 @@ type MacMachine struct {
|
||||
// LastUp contains the last recorded uptime
|
||||
LastUp time.Time
|
||||
// The VFKit endpoint where we can interact with the VM
|
||||
Vfkit VfkitHelper
|
||||
LogPath machine.VMFile
|
||||
Vfkit VfkitHelper
|
||||
LogPath machine.VMFile
|
||||
GvProxyPid machine.VMFile
|
||||
GvProxySock machine.VMFile
|
||||
}
|
||||
|
||||
func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
var (
|
||||
key string
|
||||
key string
|
||||
mounts []machine.Mount
|
||||
virtiofsMnts []machine.VirtIoFs
|
||||
)
|
||||
dataDir, err := machine.GetDataDir(machine.AppleHvVirt)
|
||||
if err != nil {
|
||||
@ -125,12 +130,22 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
m.LogPath = *logPath
|
||||
runtimeDir, err := getRuntimeDir()
|
||||
runtimeDir, err := m.getRuntimeDir()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
readySocket, err := machine.NewMachineFile(filepath.Join(runtimeDir, "podman", fmt.Sprintf("%s_ready.sock", m.Name)), nil)
|
||||
readySocket, err := machine.NewMachineFile(filepath.Join(runtimeDir, fmt.Sprintf("%s_ready.sock", m.Name)), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
gvProxyPid, err := machine.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.pid"), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
gvProxySock, err := machine.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.sock"), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -150,6 +165,8 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
}
|
||||
|
||||
m.ReadySocket = *readySocket
|
||||
m.GvProxyPid = *gvProxyPid
|
||||
m.GvProxySock = *gvProxySock
|
||||
m.Vfkit.VirtualMachine.Devices = defaultDevices
|
||||
m.Vfkit.Endpoint = defaultVFKitEndpoint
|
||||
m.Vfkit.VfkitBinaryPath = vfkitBinaryPath
|
||||
@ -166,9 +183,20 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
}
|
||||
m.Port = sshPort
|
||||
|
||||
for _, volume := range opts.Volumes {
|
||||
source, target, _, readOnly, err := machine.ParseVolumeFromPath(volume)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
mnt := machine.NewVirtIoFsMount(source, target, readOnly)
|
||||
virtiofsMnts = append(virtiofsMnts, mnt)
|
||||
mounts = append(mounts, mnt.ToMount())
|
||||
}
|
||||
m.Mounts = mounts
|
||||
|
||||
if len(opts.IgnitionPath) < 1 {
|
||||
// TODO localhost needs to be restored here
|
||||
uri := machine.SSHRemoteConnection.MakeSSHURL("192.168.64.3", fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID), strconv.Itoa(m.Port), m.RemoteUsername)
|
||||
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID), strconv.Itoa(m.Port), m.RemoteUsername)
|
||||
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(m.Port), "root")
|
||||
identity := m.IdentityPath
|
||||
|
||||
@ -252,7 +280,9 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
Name: "ready.service",
|
||||
Contents: machine.StrToPtr(fmt.Sprintf(ready, "vsock")),
|
||||
}
|
||||
virtiofsUnits := generateSystemDFilesForVirtiofsMounts(virtiofsMnts)
|
||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, readyUnit)
|
||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, virtiofsUnits...)
|
||||
|
||||
if err := ign.Write(); err != nil {
|
||||
return false, err
|
||||
@ -418,9 +448,7 @@ func (m *MacMachine) SSH(name string, opts machine.SSHOptions) error {
|
||||
if username == "" {
|
||||
username = m.RemoteUsername
|
||||
}
|
||||
// TODO when host networking is figured out, we need to switch this back to
|
||||
// machine.commonssh
|
||||
return AppleHVSSH(username, m.IdentityPath, m.Name, m.Port, opts.Args)
|
||||
return machine.CommonSSH(username, m.IdentityPath, m.Name, m.Port, opts.Args)
|
||||
}
|
||||
|
||||
func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
@ -441,22 +469,23 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
defer ioEater.Close()
|
||||
|
||||
// TODO handle returns from startHostNetworking
|
||||
_, _, err = m.startHostNetworking(ioEater)
|
||||
forwardSock, forwardState, err := m.startHostNetworking(ioEater)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add networking
|
||||
// TODO this creates a nat'd connection and we still need to switch over
|
||||
// to use gvproxy and random ssh ports.
|
||||
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)
|
||||
|
||||
for _, vol := range m.Mounts {
|
||||
virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, "podmanHomeDir")
|
||||
virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, vol.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -464,7 +493,6 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
}
|
||||
|
||||
// To start the VM, we need to call vfkit
|
||||
// TODO need to hold the start command until fcos tells us it is started
|
||||
|
||||
cmd, err := m.Vfkit.VirtualMachine.Cmd(m.Vfkit.VfkitBinaryPath.Path)
|
||||
if err != nil {
|
||||
@ -480,6 +508,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
cmd.Args = append(cmd.Args, restArgs...)
|
||||
|
||||
firstBoot, err := m.isFirstBoot()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -522,15 +551,11 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
cmd.ExtraFiles = []*os.File{ioEater, ioEater, ioEater}
|
||||
fmt.Println(cmd.Args)
|
||||
|
||||
readSocketBaseDir := filepath.Base(m.ReadySocket.GetPath())
|
||||
readSocketBaseDir := filepath.Dir(m.ReadySocket.GetPath())
|
||||
if err := os.MkdirAll(readSocketBaseDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if firstBoot {
|
||||
logrus.Debug("first boot detected")
|
||||
logrus.Debugf("serving ignition file over %s", ignitionSocket.GetPath())
|
||||
@ -550,11 +575,21 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
logrus.Debug("waiting for ready notification")
|
||||
conn, err := readyListen.Accept()
|
||||
if err != nil {
|
||||
var conn net.Conn
|
||||
readyChan := make(chan error)
|
||||
go func() {
|
||||
conn, err = readyListen.Accept()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
_, err = bufio.NewReader(conn).ReadString('\n')
|
||||
readyChan <- err
|
||||
}()
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bufio.NewReader(conn).ReadString('\n')
|
||||
|
||||
err = <-readyChan
|
||||
defer func() {
|
||||
if closeErr := conn.Close(); closeErr != nil {
|
||||
logrus.Error(closeErr)
|
||||
@ -564,6 +599,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
|
||||
return err
|
||||
}
|
||||
logrus.Debug("ready notification received")
|
||||
m.waitAPIAndPrintInfo(forwardState, forwardSock, opts.NoInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -583,6 +619,34 @@ func (m *MacMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
if vmState != machine.Running {
|
||||
return machine.ErrWrongState
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// I think "soft" errors here is OK
|
||||
gvPid, err := m.GvProxyPid.Read()
|
||||
if err != nil {
|
||||
logrus.Error(fmt.Errorf("unable to read gvproxy pid file %s: %v", m.GvProxyPid.GetPath(), err))
|
||||
return
|
||||
}
|
||||
proxyPid, err := strconv.Atoi(string(gvPid))
|
||||
if err != nil {
|
||||
logrus.Error(fmt.Errorf("unable to convert pid to integer: %v", err))
|
||||
return
|
||||
}
|
||||
proxyProc, err := os.FindProcess(proxyPid)
|
||||
if proxyProc == nil && err != nil {
|
||||
logrus.Error("unable to find process: %v", err)
|
||||
return
|
||||
}
|
||||
if err := proxyProc.Kill(); err != nil {
|
||||
logrus.Error("unable to kill gvproxy: %v", err)
|
||||
return
|
||||
}
|
||||
// gvproxy does not clean up its pid file on exit
|
||||
if err := m.GvProxyPid.Delete(); err != nil {
|
||||
logrus.Error("unable to delete gvproxy pid file: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return m.Vfkit.stop(false, true)
|
||||
}
|
||||
|
||||
@ -699,11 +763,35 @@ func (m *MacMachine) startHostNetworking(ioEater *os.File) (string, machine.APIF
|
||||
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
|
||||
}
|
||||
|
||||
attr := new(os.ProcAttr)
|
||||
gvproxy, err := cfg.FindHelperBinary("gvproxy", false)
|
||||
if err != nil {
|
||||
@ -714,12 +802,16 @@ func (m *MacMachine) startHostNetworking(ioEater *os.File) (string, machine.APIF
|
||||
cmd := []string{gvproxy}
|
||||
// Add the ssh port
|
||||
cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", m.Port)}...)
|
||||
|
||||
// Add pid file
|
||||
cmd = append(cmd, "-pid-file", m.GvProxyPid.GetPath())
|
||||
// Add vfkit proxy listen
|
||||
cmd = append(cmd, "-listen-vfkit", fmt.Sprintf("unixgram://%s", m.GvProxySock.GetPath()))
|
||||
cmd, forwardSock, state = m.setupAPIForwarding(cmd)
|
||||
if logrus.GetLevel() == logrus.DebugLevel {
|
||||
cmd = append(cmd, "--debug")
|
||||
fmt.Println(cmd)
|
||||
}
|
||||
|
||||
_, err = os.StartProcess(cmd[0], cmd, attr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("unable to execute: %q: %w", cmd, err)
|
||||
@ -770,6 +862,42 @@ func (m *MacMachine) setupAPIForwarding(cmd []string) ([]string, string, machine
|
||||
cmd = append(cmd, []string{"-forward-user", forwardUser}...)
|
||||
cmd = append(cmd, []string{"-forward-identity", 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, "", machine.MachineLocal
|
||||
}
|
||||
|
||||
@ -815,13 +943,131 @@ func (m *MacMachine) getIgnitionSock() (*machine.VMFile, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||
if !errors.Is(err, os.ErrExist) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return machine.NewMachineFile(filepath.Join(dataDir, ignitionSocketName), nil)
|
||||
}
|
||||
|
||||
func getRuntimeDir() (string, error) {
|
||||
func (m *MacMachine) getRuntimeDir() (string, error) {
|
||||
tmpDir, ok := os.LookupEnv("TMPDIR")
|
||||
if !ok {
|
||||
tmpDir = "/tmp"
|
||||
}
|
||||
return tmpDir, nil
|
||||
return filepath.Join(tmpDir, "podman"), nil
|
||||
}
|
||||
|
||||
func (m *MacMachine) userGlobalSocketLink() (string, error) {
|
||||
path, err := machine.GetDataDir(machine.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) waitAPIAndPrintInfo(forwardState machine.APIForwardingState, forwardSock string, noInfo bool) {
|
||||
suffix := ""
|
||||
if m.Name != machine.DefaultMachineName {
|
||||
suffix = " " + m.Name
|
||||
}
|
||||
|
||||
if m.isIncompatible() {
|
||||
fmt.Fprintf(os.Stderr, "\n!!! ACTION REQUIRED: INCOMPATIBLE MACHINE !!!\n")
|
||||
|
||||
fmt.Fprintf(os.Stderr, "\nThis machine was created by an older Podman release that is incompatible\n")
|
||||
fmt.Fprintf(os.Stderr, "with this release of Podman. It has been started in a limited operational\n")
|
||||
fmt.Fprintf(os.Stderr, "mode to allow you to copy any necessary files before recreating it. This\n")
|
||||
fmt.Fprintf(os.Stderr, "can be accomplished with the following commands:\n\n")
|
||||
fmt.Fprintf(os.Stderr, "\t# Login and copy desired files (Optional)\n")
|
||||
fmt.Fprintf(os.Stderr, "\t# Podman machine ssh%s tar cvPf - /path/to/files > backup.tar\n\n", suffix)
|
||||
fmt.Fprintf(os.Stderr, "\t# Recreate machine (DESTRUCTIVE!) \n")
|
||||
fmt.Fprintf(os.Stderr, "\tpodman machine stop%s\n", suffix)
|
||||
fmt.Fprintf(os.Stderr, "\tpodman machine rm -f%s\n", suffix)
|
||||
fmt.Fprintf(os.Stderr, "\tpodman machine init --now%s\n\n", suffix)
|
||||
fmt.Fprintf(os.Stderr, "\t# Copy back files (Optional)\n")
|
||||
fmt.Fprintf(os.Stderr, "\t# cat backup.tar | podman machine ssh%s tar xvPf - \n\n", suffix)
|
||||
}
|
||||
|
||||
if forwardState == machine.NoForwarding {
|
||||
return
|
||||
}
|
||||
|
||||
machine.WaitAndPingAPI(forwardSock)
|
||||
|
||||
if !noInfo {
|
||||
if !m.Rootful {
|
||||
fmt.Printf("\nThis machine is currently configured in rootless mode. If your containers\n")
|
||||
fmt.Printf("require root permissions (e.g. ports < 1024), or if you run into compatibility\n")
|
||||
fmt.Printf("issues with non-Podman clients, you can switch using the following command: \n")
|
||||
fmt.Printf("\n\tpodman machine set --rootful%s\n\n", suffix)
|
||||
}
|
||||
|
||||
fmt.Printf("API forwarding listening on: %s\n", forwardSock)
|
||||
if forwardState == machine.DockerGlobal {
|
||||
fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n")
|
||||
} else {
|
||||
stillString := "still "
|
||||
switch forwardState {
|
||||
case machine.NotInstalled:
|
||||
fmt.Printf("\nThe system helper service is not installed; the default Docker API socket\n")
|
||||
fmt.Printf("address can't be used by Podman. ")
|
||||
if helper := findClaimHelper(); len(helper) > 0 {
|
||||
fmt.Printf("If you would like to install it run the\nfollowing commands:\n")
|
||||
fmt.Printf("\n\tsudo %s install\n", helper)
|
||||
fmt.Printf("\tpodman machine stop%s; podman machine start%s\n\n", suffix, suffix)
|
||||
}
|
||||
case machine.MachineLocal:
|
||||
fmt.Printf("\nAnother process was listening on the default Docker API socket address.\n")
|
||||
case machine.ClaimUnsupported:
|
||||
fallthrough
|
||||
default:
|
||||
stillString = ""
|
||||
}
|
||||
|
||||
fmt.Printf("You can %sconnect Docker API clients by setting DOCKER_HOST using the\n", stillString)
|
||||
fmt.Printf("following command in your terminal session:\n")
|
||||
fmt.Printf("\n\texport DOCKER_HOST='unix://%s'\n\n", forwardSock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MacMachine) isIncompatible() bool {
|
||||
return m.UID == -1
|
||||
}
|
||||
|
||||
func generateSystemDFilesForVirtiofsMounts(mounts []machine.VirtIoFs) []machine.Unit {
|
||||
var unitFiles []machine.Unit
|
||||
|
||||
for _, mnt := range mounts {
|
||||
autoMountUnit := `[Automount]
|
||||
Where=%s
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
[Unit]
|
||||
Description=Mount virtiofs volume %s
|
||||
`
|
||||
mountUnit := `[Mount]
|
||||
What=%s
|
||||
Where=%s
|
||||
Type=virtiofs
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
virtiofsAutomount := machine.Unit{
|
||||
Enabled: machine.BoolToPtr(true),
|
||||
Name: fmt.Sprintf("%s.automount", mnt.Tag),
|
||||
Contents: machine.StrToPtr(fmt.Sprintf(autoMountUnit, mnt.Target, mnt.Target)),
|
||||
}
|
||||
virtiofsMount := machine.Unit{
|
||||
Enabled: machine.BoolToPtr(true),
|
||||
Name: fmt.Sprintf("%s.mount", mnt.Tag),
|
||||
Contents: machine.StrToPtr(fmt.Sprintf(mountUnit, mnt.Tag, mnt.Target)),
|
||||
}
|
||||
unitFiles = append(unitFiles, virtiofsAutomount, virtiofsMount)
|
||||
}
|
||||
return unitFiles
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package applehv
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
vfConfig "github.com/crc-org/vfkit/pkg/config"
|
||||
)
|
||||
|
||||
@ -52,3 +53,10 @@ func getDebugDevices() ([]vfConfig.VirtioDevice, error) {
|
||||
func getIgnitionVsockDevice(path string) (vfConfig.VirtioDevice, error) {
|
||||
return vfConfig.VirtioVsockNew(1024, path, true)
|
||||
}
|
||||
|
||||
func VirtIOFsToVFKitVirtIODevice(fs machine.VirtIoFs) vfConfig.VirtioFs {
|
||||
return vfConfig.VirtioFs{
|
||||
SharedDir: fs.Source,
|
||||
MountTag: fs.Tag,
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,11 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -46,6 +48,7 @@ const (
|
||||
// Starting indicated the vm is in the process of starting
|
||||
Starting Status = "starting"
|
||||
DefaultMachineName string = "podman-machine-default"
|
||||
apiUpTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
type RemoteConnectionType string
|
||||
@ -454,3 +457,28 @@ func NewVirtualization(artifact Artifact, compression ImageCompression, format I
|
||||
format,
|
||||
}
|
||||
}
|
||||
|
||||
func WaitAndPingAPI(sock string) {
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
||||
con, err := net.DialTimeout("unix", sock, apiUpTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := con.SetDeadline(time.Now().Add(apiUpTimeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return con, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := client.Get("http://host/_ping")
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
logrus.Warn("API socket failed ping test")
|
||||
}
|
||||
}
|
||||
|
35
pkg/machine/machine_unix.go
Normal file
35
pkg/machine/machine_unix.go
Normal file
@ -0,0 +1,35 @@
|
||||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseVolumeFromPath is a oneshot parsing of a provided volume. It follows the "rules" of
|
||||
// the singular parsing functions
|
||||
func ParseVolumeFromPath(v string) (source, target, options string, readonly bool, err error) {
|
||||
split := strings.SplitN(v, ":", 3)
|
||||
switch len(split) {
|
||||
case 1:
|
||||
source = split[0]
|
||||
target = split[0]
|
||||
case 2:
|
||||
source = split[0]
|
||||
target = split[1]
|
||||
case 3:
|
||||
source = split[0]
|
||||
target = split[1]
|
||||
options = split[2]
|
||||
default:
|
||||
return "", "", "", false, errors.New("invalid volume provided")
|
||||
}
|
||||
|
||||
// I suppose an option not intended for read-only could interfere here but I do not see a better way
|
||||
if strings.Contains(options, "ro") {
|
||||
readonly = true
|
||||
}
|
||||
return
|
||||
}
|
@ -6,14 +6,12 @@ package qemu
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -44,7 +42,6 @@ const (
|
||||
MountType9p = "9p"
|
||||
dockerSock = "/var/run/docker.sock"
|
||||
dockerConnectTimeout = 5 * time.Second
|
||||
apiUpTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
type MachineVM struct {
|
||||
@ -1287,31 +1284,6 @@ func alreadyLinked(target string, link string) bool {
|
||||
return err == nil && read == target
|
||||
}
|
||||
|
||||
func waitAndPingAPI(sock string) {
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
||||
con, err := net.DialTimeout("unix", sock, apiUpTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := con.SetDeadline(time.Now().Add(apiUpTimeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return con, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := client.Get("http://host/_ping")
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
logrus.Warn("API socket failed ping test")
|
||||
}
|
||||
}
|
||||
|
||||
func (v *MachineVM) waitAPIAndPrintInfo(forwardState machine.APIForwardingState, forwardSock string, noInfo bool) {
|
||||
suffix := ""
|
||||
if v.Name != machine.DefaultMachineName {
|
||||
@ -1339,7 +1311,7 @@ func (v *MachineVM) waitAPIAndPrintInfo(forwardState machine.APIForwardingState,
|
||||
return
|
||||
}
|
||||
|
||||
waitAndPingAPI(forwardSock)
|
||||
machine.WaitAndPingAPI(forwardSock)
|
||||
|
||||
if !noInfo {
|
||||
if !v.Rootful {
|
||||
|
61
pkg/machine/volumes.go
Normal file
61
pkg/machine/volumes.go
Normal file
@ -0,0 +1,61 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Volume interface {
|
||||
Kind() VolumeKind
|
||||
}
|
||||
|
||||
type VolumeKind string
|
||||
|
||||
var (
|
||||
VirtIOFsVk VolumeKind = "virtiofs"
|
||||
NinePVk VolumeKind = "9p"
|
||||
)
|
||||
|
||||
type VirtIoFs struct {
|
||||
VolumeKind
|
||||
ReadOnly bool
|
||||
Source string
|
||||
Tag string
|
||||
Target string
|
||||
}
|
||||
|
||||
func (v VirtIoFs) Kind() string {
|
||||
return string(VirtIOFsVk)
|
||||
}
|
||||
|
||||
// unitName is the fq path where /'s are replaced with -'s
|
||||
func (v VirtIoFs) unitName() string {
|
||||
// delete the leading -
|
||||
unit := strings.ReplaceAll(v.Target, "/", "-")
|
||||
if strings.HasPrefix(unit, "-") {
|
||||
return unit[1:]
|
||||
}
|
||||
return unit
|
||||
}
|
||||
|
||||
func (v VirtIoFs) ToMount() Mount {
|
||||
return Mount{
|
||||
ReadOnly: v.ReadOnly,
|
||||
Source: v.Source,
|
||||
Tag: v.Tag,
|
||||
Target: v.Target,
|
||||
Type: v.Kind(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewVirtIoFsMount describes a machine volume mount for virtio-fs. With virtio-fs
|
||||
// the source/target are described as a "shared dir". With this style of volume mount
|
||||
// the Tag is used as the descriptor value for the mount (in Linux).
|
||||
func NewVirtIoFsMount(src, target string, readOnly bool) VirtIoFs {
|
||||
vfs := VirtIoFs{
|
||||
ReadOnly: readOnly,
|
||||
Source: src,
|
||||
Target: target,
|
||||
}
|
||||
vfs.Tag = vfs.unitName()
|
||||
return vfs
|
||||
}
|
Reference in New Issue
Block a user