mirror of
https://github.com/containers/podman.git
synced 2025-06-07 15:48:37 +08:00
MacOS improvements
* Enable support of virtfs in Podman and darwin. At the time of this writing, it requires a special patch not yet included in upstream qemu. * Prefer to use a specially built qemu to support virtfs. The qemu is installed under libexec/podman. [NO NEW TESTS NEEDED] Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
|
//go:build amd64 || arm64
|
||||||
// +build amd64 arm64
|
// +build amd64 arm64
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
@ -28,6 +29,8 @@ type InitOptions struct {
|
|||||||
Username string
|
Username string
|
||||||
ReExec bool
|
ReExec bool
|
||||||
Rootful bool
|
Rootful bool
|
||||||
|
// The numberical userid of the user that called machine
|
||||||
|
UID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type QemuMachineStatus = string
|
type QemuMachineStatus = string
|
||||||
|
@ -51,6 +51,7 @@ type DynamicIgnition struct {
|
|||||||
Name string
|
Name string
|
||||||
Key string
|
Key string
|
||||||
TimeZone string
|
TimeZone string
|
||||||
|
UID int
|
||||||
VMName string
|
VMName string
|
||||||
WritePath string
|
WritePath string
|
||||||
}
|
}
|
||||||
@ -63,12 +64,13 @@ func NewIgnitionFile(ign DynamicIgnition) error {
|
|||||||
ignVersion := Ignition{
|
ignVersion := Ignition{
|
||||||
Version: "3.2.0",
|
Version: "3.2.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
ignPassword := Passwd{
|
ignPassword := Passwd{
|
||||||
Users: []PasswdUser{
|
Users: []PasswdUser{
|
||||||
{
|
{
|
||||||
Name: ign.Name,
|
Name: ign.Name,
|
||||||
SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)},
|
SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)},
|
||||||
|
// Set the UID of the core user inside the machine
|
||||||
|
UID: intToPtr(ign.UID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root",
|
Name: "root",
|
||||||
@ -289,9 +291,7 @@ func getDirs(usrName string) []Directory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getFiles(usrName string) []File {
|
func getFiles(usrName string) []File {
|
||||||
var (
|
files := make([]File, 0)
|
||||||
files []File
|
|
||||||
)
|
|
||||||
|
|
||||||
lingerExample := `[Unit]
|
lingerExample := `[Unit]
|
||||||
Description=A systemd user unit demo
|
Description=A systemd user unit demo
|
||||||
@ -310,6 +310,7 @@ machine_enabled=true
|
|||||||
delegateConf := `[Service]
|
delegateConf := `[Service]
|
||||||
Delegate=memory pids cpu io
|
Delegate=memory pids cpu io
|
||||||
`
|
`
|
||||||
|
subUID := `%s:100000:1000000`
|
||||||
|
|
||||||
// Add a fake systemd service to get the user socket rolling
|
// Add a fake systemd service to get the user socket rolling
|
||||||
files = append(files, File{
|
files = append(files, File{
|
||||||
@ -344,6 +345,25 @@ Delegate=memory pids cpu io
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Setup /etc/subuid and /etc/subgid
|
||||||
|
for _, sub := range []string{"/etc/subuid", "/etc/subgid"} {
|
||||||
|
files = append(files, File{
|
||||||
|
Node: Node{
|
||||||
|
Group: getNodeGrp("root"),
|
||||||
|
Path: sub,
|
||||||
|
User: getNodeUsr("root"),
|
||||||
|
Overwrite: boolToPtr(true),
|
||||||
|
},
|
||||||
|
FileEmbedded1: FileEmbedded1{
|
||||||
|
Append: nil,
|
||||||
|
Contents: Resource{
|
||||||
|
Source: encodeDataURLPtr(fmt.Sprintf(subUID, usrName)),
|
||||||
|
},
|
||||||
|
Mode: intToPtr(0744),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Set delegate.conf so cpu,io subsystem is delegated to non-root users as well for cgroupv2
|
// Set delegate.conf so cpu,io subsystem is delegated to non-root users as well for cgroupv2
|
||||||
// by default
|
// by default
|
||||||
files = append(files, File{
|
files = append(files, File{
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
//go:build (amd64 && !windows) || (arm64 && !windows)
|
||||||
// +build amd64,!windows arm64,!windows
|
// +build amd64,!windows arm64,!windows
|
||||||
|
|
||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Provider struct{}
|
type Provider struct{}
|
||||||
|
|
||||||
@ -35,6 +38,8 @@ type MachineVM struct {
|
|||||||
RemoteUsername string
|
RemoteUsername string
|
||||||
// Whether this machine should run in a rootful or rootless manner
|
// Whether this machine should run in a rootful or rootless manner
|
||||||
Rootful bool
|
Rootful bool
|
||||||
|
// UID is the numerical id of the user that called machine
|
||||||
|
UID int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mount struct {
|
type Mount struct {
|
||||||
|
@ -88,11 +88,16 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
|||||||
vm.Memory = opts.Memory
|
vm.Memory = opts.Memory
|
||||||
vm.DiskSize = opts.DiskSize
|
vm.DiskSize = opts.DiskSize
|
||||||
|
|
||||||
// Look up the executable
|
// Find the qemu executable
|
||||||
execPath, err := exec.LookPath(QemuCommand)
|
cfg, err := config.Default()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
execPath, err := cfg.FindHelperBinary(QemuCommand, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
cmd := append([]string{execPath})
|
cmd := append([]string{execPath})
|
||||||
// Add memory
|
// Add memory
|
||||||
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
|
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
|
||||||
@ -245,12 +250,13 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.Mounts = mounts
|
v.Mounts = mounts
|
||||||
|
v.UID = os.Getuid()
|
||||||
|
|
||||||
// Add location of bootable image
|
// Add location of bootable image
|
||||||
v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath)
|
v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath)
|
||||||
// This kind of stinks but no other way around this r/n
|
// This kind of stinks but no other way around this r/n
|
||||||
if len(opts.IgnitionPath) < 1 {
|
if len(opts.IgnitionPath) < 1 {
|
||||||
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername)
|
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID), strconv.Itoa(v.Port), v.RemoteUsername)
|
||||||
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root")
|
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root")
|
||||||
identity := filepath.Join(sshDir, v.Name)
|
identity := filepath.Join(sshDir, v.Name)
|
||||||
|
|
||||||
@ -296,7 +302,16 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
// only if the virtualdisk size is less than
|
// only if the virtualdisk size is less than
|
||||||
// the given disk size
|
// the given disk size
|
||||||
if opts.DiskSize<<(10*3) > originalDiskSize {
|
if opts.DiskSize<<(10*3) > originalDiskSize {
|
||||||
resize := exec.Command("qemu-img", []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
|
// Find the qemu executable
|
||||||
|
cfg, err := config.Default()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
resizePath, err := cfg.FindHelperBinary("qemu-img", true)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
resize := exec.Command(resizePath, []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
|
||||||
resize.Stdout = os.Stdout
|
resize.Stdout = os.Stdout
|
||||||
resize.Stderr = os.Stderr
|
resize.Stderr = os.Stderr
|
||||||
if err := resize.Run(); err != nil {
|
if err := resize.Run(); err != nil {
|
||||||
@ -319,6 +334,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
VMName: v.Name,
|
VMName: v.Name,
|
||||||
TimeZone: opts.TimeZone,
|
TimeZone: opts.TimeZone,
|
||||||
WritePath: v.IgnitionFilePath,
|
WritePath: v.IgnitionFilePath,
|
||||||
|
UID: v.UID,
|
||||||
}
|
}
|
||||||
err = machine.NewIgnitionFile(ign)
|
err = machine.NewIgnitionFile(ign)
|
||||||
return err == nil, err
|
return err == nil, err
|
||||||
@ -459,7 +475,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
|||||||
for _, mount := range v.Mounts {
|
for _, mount := range v.Mounts {
|
||||||
fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target)
|
fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target)
|
||||||
// create mountpoint directory if it doesn't exist
|
// create mountpoint directory if it doesn't exist
|
||||||
err = v.SSH(name, machine.SSHOptions{Args: []string{"-q", "--", "sudo", "mkdir", "-p", mount.Target}})
|
// because / is immutable, we have to monkey around with permissions
|
||||||
|
// if we dont mount in /home or /mnt
|
||||||
|
args := []string{"-q", "--"}
|
||||||
|
if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") {
|
||||||
|
args = append(args, "sudo", "chattr", "-i", "/", ";")
|
||||||
|
}
|
||||||
|
args = append(args, "sudo", "mkdir", "-p", mount.Target)
|
||||||
|
if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") {
|
||||||
|
args = append(args, ";", "sudo", "chattr", "+i", "/", ";")
|
||||||
|
}
|
||||||
|
err = v.SSH(name, machine.SSHOptions{Args: args})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -795,7 +821,16 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
|
|||||||
// executes qemu-image info to get the virtual disk size
|
// executes qemu-image info to get the virtual disk size
|
||||||
// of the diskimage
|
// of the diskimage
|
||||||
func getDiskSize(path string) (uint64, error) {
|
func getDiskSize(path string) (uint64, error) {
|
||||||
diskInfo := exec.Command("qemu-img", "info", "--output", "json", path)
|
// Find the qemu executable
|
||||||
|
cfg, err := config.Default()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
qemuPathDir, err := cfg.FindHelperBinary("qemu-img", true)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
diskInfo := exec.Command(qemuPathDir, "info", "--output", "json", path)
|
||||||
stdout, err := diskInfo.StdoutPipe()
|
stdout, err := diskInfo.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -957,7 +992,7 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa
|
|||||||
return cmd, "", noForwarding
|
return cmd, "", noForwarding
|
||||||
}
|
}
|
||||||
|
|
||||||
destSock := "/run/user/1000/podman/podman.sock"
|
destSock := fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID)
|
||||||
forwardUser := "core"
|
forwardUser := "core"
|
||||||
|
|
||||||
if v.Rootful {
|
if v.Rootful {
|
||||||
|
@ -45,6 +45,7 @@ func getOvmfDir(imagePath, vmName string) string {
|
|||||||
*/
|
*/
|
||||||
func getEdk2CodeFd(name string) string {
|
func getEdk2CodeFd(name string) string {
|
||||||
dirs := []string{
|
dirs := []string{
|
||||||
|
"/opt/homebrew/opt/podman/libexec/share/qemu",
|
||||||
"/usr/local/share/qemu",
|
"/usr/local/share/qemu",
|
||||||
"/opt/homebrew/share/qemu",
|
"/opt/homebrew/share/qemu",
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user