Merge pull request #13750 from baude/refactor3

machine refactor 3: add symlinks for sockets
This commit is contained in:
OpenShift Merge Robot 2022-04-06 03:20:50 -04:00 committed by GitHub
commit 370821f3e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 59 deletions

View File

@ -7,6 +7,7 @@ import (
"errors" "errors"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"time" "time"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -20,6 +21,9 @@ const (
Next string = "next" Next string = "next"
// Stable FCOS stream // Stable FCOS stream
Stable string = "stable" Stable string = "stable"
// Max length of fully qualified socket path
maxSocketPathLength int = 103
) )
type Provider struct{} type Provider struct{}
@ -197,8 +201,27 @@ func NewMachineFile(path string, symlink *string) (*MachineFile, error) {
if symlink != nil && len(*symlink) < 1 { if symlink != nil && len(*symlink) < 1 {
return nil, errors.New("invalid symlink path") return nil, errors.New("invalid symlink path")
} }
return &MachineFile{ mf := MachineFile{Path: path}
Path: path, if symlink != nil && len(path) > maxSocketPathLength {
Symlink: symlink, if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) {
}, nil return nil, err
}
}
return &mf, nil
}
// makeSymlink for macOS creates a symlink in $HOME/.podman/
// for a machinefile like a socket
func (m *MachineFile) makeSymlink(symlink *string) error {
homedir, err := os.UserHomeDir()
if err != nil {
return err
}
sl := filepath.Join(homedir, ".podman", *symlink)
// make the symlink dir and throw away if it already exists
if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
m.Symlink = &sl
return os.Symlink(m.Path, sl)
} }

View File

@ -1,8 +1,12 @@
package qemu package qemu
import ( import (
"os"
"path/filepath"
"reflect" "reflect"
"testing" "testing"
"github.com/containers/podman/v4/test/utils"
) )
func TestMachineFile_GetPath(t *testing.T) { func TestMachineFile_GetPath(t *testing.T) {
@ -45,10 +49,30 @@ func TestMachineFile_GetPath(t *testing.T) {
} }
func TestNewMachineFile(t *testing.T) { func TestNewMachineFile(t *testing.T) {
p := "/var/tmp/podman/my.sock"
sym := "/tmp/podman/my.sock"
empty := "" empty := ""
homedir, err := os.MkdirTemp("/tmp", "homedir")
if err != nil {
panic(err)
}
defer os.RemoveAll(homedir)
longTemp, err := os.MkdirTemp("/tmp", "tmpdir")
if err != nil {
panic(err)
}
defer os.RemoveAll(longTemp)
oldhome := os.Getenv("HOME")
os.Setenv("HOME", homedir) //nolint: tenv
defer os.Setenv("HOME", oldhome)
p := "/var/tmp/podman/my.sock"
longp := filepath.Join(longTemp, utils.RandomString(100), "my.sock")
os.MkdirAll(filepath.Dir(longp), 0755)
f, _ := os.Create(longp)
f.Close()
sym := "my.sock"
longSym := filepath.Join(homedir, ".podman", sym)
m := MachineFile{ m := MachineFile{
Path: p, Path: p,
Symlink: nil, Symlink: nil,
@ -70,9 +94,9 @@ func TestNewMachineFile(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "Good with Symlink", name: "Good with short symlink",
args: args{p, &sym}, args: args{p, &sym},
want: &MachineFile{p, &sym}, want: &MachineFile{p, nil},
wantErr: false, wantErr: false,
}, },
{ {
@ -87,6 +111,12 @@ func TestNewMachineFile(t *testing.T) {
want: nil, want: nil,
wantErr: true, wantErr: true,
}, },
{
name: "Good with long symlink",
args: args{longp, &sym},
want: &MachineFile{longp, &longSym},
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -131,10 +131,11 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
// Add serial port for readiness // Add serial port for readiness
cmd = append(cmd, []string{ cmd = append(cmd, []string{
"-device", "virtio-serial", "-device", "virtio-serial",
"-chardev", "socket,path=" + vm.getReadySocket() + ",server=on,wait=off,id=" + vm.Name + "_ready", // qemu needs to establish the long name; other connections can use the symlink'd
"-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=" + vm.Name + "_ready",
"-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...) "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...)
vm.CmdLine = cmd vm.CmdLine = cmd
if err := vm.setQEMUAndPIDSocket(); err != nil { if err := vm.setPIDSocket(); err != nil {
return nil, err return nil, err
} }
return vm, nil return vm, nil
@ -487,9 +488,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return err return err
} }
} }
if err := v.setQEMUAndPIDSocket(); err != nil {
return err
}
// If the qemusocketpath exists and the vm is off/down, we should rm // If the qemusocketpath exists and the vm is off/down, we should rm
// it before the dial as to avoid a segv // it before the dial as to avoid a segv
if err := v.QMPMonitor.Address.Delete(); err != nil { if err := v.QMPMonitor.Address.Delete(); err != nil {
@ -700,7 +698,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
return err return err
} }
if _, err := os.Stat(v.getPidFile()); os.IsNotExist(err) { if _, err := os.Stat(v.PidFilePath.GetPath()); os.IsNotExist(err) {
return nil return nil
} }
pidString, err := v.PidFilePath.Read() pidString, err := v.PidFilePath.Read()
@ -748,7 +746,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
waitInternal = waitInternal * 2 waitInternal = waitInternal * 2
} }
return nil return v.ReadySocket.Delete()
} }
// NewQMPMonitor creates the monitor subsection of our vm // NewQMPMonitor creates the monitor subsection of our vm
@ -770,7 +768,7 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
if timeout == 0 { if timeout == 0 {
timeout = defaultQMPTimeout timeout = defaultQMPTimeout
} }
address, err := NewMachineFile(filepath.Join(rtDir, "qmp+"+name+".sock"), nil) address, err := NewMachineFile(filepath.Join(rtDir, "qmp_"+name+".sock"), nil)
if err != nil { if err != nil {
return Monitor{}, err return Monitor{}, err
} }
@ -807,11 +805,14 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
if !opts.SaveImage { if !opts.SaveImage {
files = append(files, v.getImageFile()) files = append(files, v.getImageFile())
} }
socketPath, err := v.getForwardSocketPath() socketPath, err := v.forwardSocketPath()
if err != nil { if err != nil {
logrus.Error(err) return "", nil, err
} }
files = append(files, socketPath) if socketPath.Symlink != nil {
files = append(files, *socketPath.Symlink)
}
files = append(files, socketPath.Path)
files = append(files, v.archRemovalFiles()...) files = append(files, v.archRemovalFiles()...)
if err := machine.RemoveConnection(v.Name); err != nil { if err := machine.RemoveConnection(v.Name); err != nil {
@ -831,7 +832,6 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
confirmationMessage += msg + "\n" confirmationMessage += msg + "\n"
} }
//silently try to delete socket and pid file
//remove socket and pid file if any: warn at low priority if things fail //remove socket and pid file if any: warn at low priority if things fail
// Remove the pidfile // Remove the pidfile
if err := v.PidFilePath.Delete(); err != nil { if err := v.PidFilePath.Delete(); err != nil {
@ -1083,7 +1083,7 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
files := []*os.File{os.Stdin, os.Stdout, os.Stderr} files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
attr.Files = files attr.Files = files
cmd := []string{binary} cmd := []string{binary}
cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.getQMPMonitorSocket()), "-pid-file", v.getPidFile()}...) cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.QMPMonitor.Address.GetPath()), "-pid-file", v.PidFilePath.GetPath()}...)
// Add the ssh port // Add the ssh port
cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...)
@ -1102,7 +1102,7 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
} }
func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) { func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) {
socket, err := v.getForwardSocketPath() socket, err := v.forwardSocketPath()
if err != nil { if err != nil {
return cmd, "", noForwarding return cmd, "", noForwarding
@ -1116,43 +1116,43 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa
forwardUser = "root" forwardUser = "root"
} }
cmd = append(cmd, []string{"-forward-sock", socket}...) cmd = append(cmd, []string{"-forward-sock", socket.GetPath()}...)
cmd = append(cmd, []string{"-forward-dest", destSock}...) cmd = append(cmd, []string{"-forward-dest", destSock}...)
cmd = append(cmd, []string{"-forward-user", forwardUser}...) cmd = append(cmd, []string{"-forward-user", forwardUser}...)
cmd = append(cmd, []string{"-forward-identity", v.IdentityPath}...) cmd = append(cmd, []string{"-forward-identity", v.IdentityPath}...)
link := filepath.Join(filepath.Dir(filepath.Dir(socket)), "podman.sock") link := socket.GetPath()
// The linking pattern is /var/run/docker.sock -> user global sock (link) -> machine sock (socket) // The linking pattern is /var/run/docker.sock -> user global sock (link) -> machine sock (socket)
// This allows the helper to only have to maintain one constant target to the user, which can be // This allows the helper to only have to maintain one constant target to the user, which can be
// repositioned without updating docker.sock. // repositioned without updating docker.sock.
if !dockerClaimSupported() { if !dockerClaimSupported() {
return cmd, socket, claimUnsupported return cmd, socket.GetPath(), claimUnsupported
} }
if !dockerClaimHelperInstalled() { if !dockerClaimHelperInstalled() {
return cmd, socket, notInstalled return cmd, socket.GetPath(), notInstalled
} }
if !alreadyLinked(socket, link) { if !alreadyLinked(socket.GetPath(), link) {
if checkSockInUse(link) { if checkSockInUse(link) {
return cmd, socket, machineLocal return cmd, socket.GetPath(), machineLocal
} }
_ = os.Remove(link) _ = os.Remove(link)
if err = os.Symlink(socket, link); err != nil { if err = os.Symlink(socket.GetPath(), link); err != nil {
logrus.Warnf("could not create user global API forwarding link: %s", err.Error()) logrus.Warnf("could not create user global API forwarding link: %s", err.Error())
return cmd, socket, machineLocal return cmd, socket.GetPath(), machineLocal
} }
} }
if !alreadyLinked(link, dockerSock) { if !alreadyLinked(link, dockerSock) {
if checkSockInUse(dockerSock) { if checkSockInUse(dockerSock) {
return cmd, socket, machineLocal return cmd, socket.GetPath(), machineLocal
} }
if !claimDockerSock() { if !claimDockerSock() {
logrus.Warn("podman helper is installed, but was not able to claim the global docker sock") logrus.Warn("podman helper is installed, but was not able to claim the global docker sock")
return cmd, socket, machineLocal return cmd, socket.GetPath(), machineLocal
} }
} }
@ -1163,13 +1163,14 @@ func (v *MachineVM) isIncompatible() bool {
return v.UID == -1 return v.UID == -1
} }
func (v *MachineVM) getForwardSocketPath() (string, error) { func (v *MachineVM) forwardSocketPath() (*MachineFile, error) {
sockName := "podman.sock"
path, err := machine.GetDataDir(v.Name) path, err := machine.GetDataDir(v.Name)
if err != nil { if err != nil {
logrus.Errorf("Resolving data dir: %s", err.Error()) logrus.Errorf("Resolving data dir: %s", err.Error())
return "", nil return nil, err
} }
return filepath.Join(path, "podman.sock"), nil return NewMachineFile(filepath.Join(path, sockName), &sockName)
} }
func (v *MachineVM) setConfigPath() error { func (v *MachineVM) setConfigPath() error {
@ -1187,11 +1188,12 @@ func (v *MachineVM) setConfigPath() error {
} }
func (v *MachineVM) setReadySocket() error { func (v *MachineVM) setReadySocket() error {
readySocketName := v.Name + "_ready.sock"
rtPath, err := getRuntimeDir() rtPath, err := getRuntimeDir()
if err != nil { if err != nil {
return err return err
} }
virtualSocketPath, err := NewMachineFile(filepath.Join(rtPath, "podman", v.Name+"_ready.sock"), nil) virtualSocketPath, err := NewMachineFile(filepath.Join(rtPath, "podman", readySocketName), &readySocketName)
if err != nil { if err != nil {
return err return err
} }
@ -1199,7 +1201,7 @@ func (v *MachineVM) setReadySocket() error {
return nil return nil
} }
func (v *MachineVM) setQEMUAndPIDSocket() error { func (v *MachineVM) setPIDSocket() error {
rtPath, err := getRuntimeDir() rtPath, err := getRuntimeDir()
if err != nil { if err != nil {
return err return err
@ -1207,8 +1209,9 @@ func (v *MachineVM) setQEMUAndPIDSocket() error {
if !rootless.IsRootless() { if !rootless.IsRootless() {
rtPath = "/run" rtPath = "/run"
} }
pidFileName := fmt.Sprintf("%s.pid", v.Name)
socketDir := filepath.Join(rtPath, "podman") socketDir := filepath.Join(rtPath, "podman")
pidFilePath, err := NewMachineFile(filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)), nil) pidFilePath, err := NewMachineFile(filepath.Join(socketDir, pidFileName), &pidFileName)
if err != nil { if err != nil {
return err return err
} }
@ -1216,7 +1219,7 @@ func (v *MachineVM) setQEMUAndPIDSocket() error {
return nil return nil
} }
// Deprecated: getSocketandPid is being replace by setQEMUAndPIDSocket and // Deprecated: getSocketandPid is being replace by setPIDSocket and
// machinefiles. // machinefiles.
func (v *MachineVM) getSocketandPid() (string, string, error) { func (v *MachineVM) getSocketandPid() (string, string, error) {
rtPath, err := getRuntimeDir() rtPath, err := getRuntimeDir()
@ -1342,7 +1345,7 @@ func (v *MachineVM) update() error {
} }
b, err := v.ConfigPath.Read() b, err := v.ConfigPath.Read()
if err != nil { if err != nil {
if os.IsNotExist(err) { if errors.Is(err, os.ErrNotExist) {
return errors.Wrap(machine.ErrNoSuchVM, v.Name) return errors.Wrap(machine.ErrNoSuchVM, v.Name)
} }
return err return err
@ -1377,30 +1380,13 @@ func (v *MachineVM) writeConfig() error {
return nil return nil
} }
// getPidFile gets the file where the machine pid is stored // getImageFile wrapper returns the path to the image used
func (v *MachineVM) getPidFile() string {
return v.PidFilePath.GetPath()
}
// getQMPMonitorSocket gets the socket used by qemu to interact
// with the instance
func (v *MachineVM) getQMPMonitorSocket() string {
return v.QMPMonitor.Address.GetPath()
}
// getReadySocket returns the socket used to communicate
// with the machinevm and report when it is booted
func (v *MachineVM) getReadySocket() string {
return v.ReadySocket.GetPath()
}
// getImageFile returns the path to the image used
// to boot the VM // to boot the VM
func (v *MachineVM) getImageFile() string { func (v *MachineVM) getImageFile() string {
return v.ImagePath.GetPath() return v.ImagePath.GetPath()
} }
// getIgnitionFile returns the path to the ignition file // getIgnitionFile wrapper returns the path to the ignition file
func (v *MachineVM) getIgnitionFile() string { func (v *MachineVM) getIgnitionFile() string {
return v.IgnitionFilePath.GetPath() return v.IgnitionFilePath.GetPath()
} }