mirror of
https://github.com/containers/podman.git
synced 2025-07-01 16:17:06 +08:00
Add Rosetta support for Apple Silicon mac
Signed-off-by: Shion Tanaka <shtanaka@redhat.com>
This commit is contained in:
@ -74,6 +74,11 @@ func inspect(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
rosetta, err := provider.GetRosetta(mc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ii := machine.InspectInfo{
|
||||
ConfigDir: *dirs.ConfigDir,
|
||||
ConnectionInfo: machine.ConnectionConfig{
|
||||
@ -88,6 +93,7 @@ func inspect(cmd *cobra.Command, args []string) error {
|
||||
State: state,
|
||||
UserModeNetworking: provider.UserModeNetworkEnabled(mc),
|
||||
Rootful: mc.HostUser.Rootful,
|
||||
Rosetta: rosetta,
|
||||
}
|
||||
|
||||
vms = append(vms, ii)
|
||||
|
@ -32,6 +32,7 @@ Print results with a Go template.
|
||||
| .Name | Name of the machine |
|
||||
| .Resources ... | Resources used by the machine |
|
||||
| .Rootful | Whether the machine prefers rootful or rootless container execution |
|
||||
| .Rosetta | Whether this machine uses Rosetta |
|
||||
| .SSHConfig ... | SSH configuration info for communicating with machine |
|
||||
| .State | Machine state |
|
||||
| .UserModeNetworking | Whether this machine uses user-mode networking |
|
||||
|
@ -42,6 +42,18 @@ func GetDefaultDevices(mc *vmconfigs.MachineConfig) ([]vfConfig.VirtioDevice, *d
|
||||
return nil, nil, err
|
||||
}
|
||||
devices = append(devices, disk, rng, serial, readyDevice)
|
||||
|
||||
rosettaCfg := mc.AppleHypervisor.Vfkit.Rosetta
|
||||
if rosettaCfg {
|
||||
rosetta := &vfConfig.RosettaShare{
|
||||
DirectorySharingConfig: vfConfig.DirectorySharingConfig{
|
||||
MountTag: define.MountTag,
|
||||
},
|
||||
InstallRosetta: true,
|
||||
}
|
||||
devices = append(devices, rosetta)
|
||||
}
|
||||
|
||||
return devices, readySocket, nil
|
||||
}
|
||||
|
||||
|
@ -124,4 +124,5 @@ type Helper struct {
|
||||
Endpoint string
|
||||
BinaryPath *define.VMFile
|
||||
VirtualMachine *config.VirtualMachine
|
||||
Rosetta bool
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ package applehv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||
"github.com/containers/podman/v5/pkg/machine"
|
||||
"github.com/containers/podman/v5/pkg/machine/apple"
|
||||
@ -65,6 +67,16 @@ func (a AppleHVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.Machine
|
||||
}
|
||||
ignBuilder.WithUnit(virtIOIgnitionMounts...)
|
||||
|
||||
cfg, err := config.Default()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rosetta := cfg.Machine.Rosetta
|
||||
if runtime.GOARCH != "arm64" {
|
||||
rosetta = false
|
||||
}
|
||||
mc.AppleHypervisor.Vfkit.Rosetta = rosetta
|
||||
|
||||
return apple.ResizeDisk(mc, mc.Resources.DiskSize)
|
||||
}
|
||||
|
||||
@ -104,6 +116,18 @@ func (a AppleHVStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func
|
||||
return nil, nil, fmt.Errorf("unable to determine boot loader for this machine")
|
||||
}
|
||||
|
||||
cfg, err := config.Default()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rosetta := cfg.Machine.Rosetta
|
||||
rosettaNew := rosetta
|
||||
if runtime.GOARCH == "arm64" {
|
||||
rosettaMC := mc.AppleHypervisor.Vfkit.Rosetta
|
||||
if rosettaMC != rosettaNew {
|
||||
mc.AppleHypervisor.Vfkit.Rosetta = rosettaNew
|
||||
}
|
||||
}
|
||||
return apple.StartGenericAppleVM(mc, vfkitCommand, bl, mc.AppleHypervisor.Vfkit.Endpoint)
|
||||
}
|
||||
|
||||
@ -131,3 +155,8 @@ func (a AppleHVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo
|
||||
func (a AppleHVStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, a.VMType(), mc.Name)
|
||||
}
|
||||
|
||||
func (a *AppleHVStubber) GetRosetta(mc *vmconfigs.MachineConfig) (bool, error) {
|
||||
rosetta := mc.AppleHypervisor.Vfkit.Rosetta
|
||||
return rosetta, nil
|
||||
}
|
||||
|
@ -71,8 +71,9 @@ type SSHOptions struct {
|
||||
}
|
||||
|
||||
type StartOptions struct {
|
||||
NoInfo bool
|
||||
Quiet bool
|
||||
NoInfo bool
|
||||
Quiet bool
|
||||
Rosetta bool
|
||||
}
|
||||
|
||||
type StopOptions struct{}
|
||||
@ -117,6 +118,7 @@ type InspectInfo struct {
|
||||
State define.Status
|
||||
UserModeNetworking bool
|
||||
Rootful bool
|
||||
Rosetta bool
|
||||
}
|
||||
|
||||
// ImageConfig describes the bootable image for the VM
|
||||
|
@ -5,6 +5,10 @@ import "os"
|
||||
const UserCertsTargetPath = "/etc/containers/certs.d"
|
||||
const DefaultIdentityName = "machine"
|
||||
|
||||
// MountTag is an identifier to mount a VirtioFS file system tag on a mount point in the VM.
|
||||
// Ref: https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta
|
||||
const MountTag = "rosetta"
|
||||
|
||||
var (
|
||||
DefaultFilePerm os.FileMode = 0644
|
||||
)
|
||||
|
@ -370,6 +370,100 @@ var _ = Describe("podman machine init", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(inspectShouldPass).To(Exit(0))
|
||||
})
|
||||
|
||||
It("machine init with rosetta=true", func() {
|
||||
skipIfVmtype(define.QemuVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.WSLVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.HyperVVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.LibKrun, "Test is only for AppleHv")
|
||||
if runtime.GOARCH != "arm64" {
|
||||
Skip("Test is only for AppleHv with arm64 architecture")
|
||||
}
|
||||
|
||||
i := initMachine{}
|
||||
name := randomString()
|
||||
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath)).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(session).To(Exit(0))
|
||||
|
||||
s := startMachine{}
|
||||
ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ssession).Should(Exit(0))
|
||||
|
||||
inspect := new(inspectMachine)
|
||||
inspect = inspect.withFormat("{{.Rosetta}}")
|
||||
inspectSession, err := mb.setName(name).setCmd(inspect).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(inspectSession).To(Exit(0))
|
||||
Expect(inspectSession.outputToString()).To(Equal("true"))
|
||||
|
||||
mnt := sshMachine{}
|
||||
mntSession, err := mb.setName(name).setCmd(mnt.withSSHCommand([]string{"ls -d /mnt/rosetta"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mntSession).To(Exit(0))
|
||||
Expect(mntSession.outputToString()).To(ContainSubstring("/mnt/rosetta"))
|
||||
|
||||
proc := sshMachine{}
|
||||
procSession, err := mb.setName(name).setCmd(proc.withSSHCommand([]string{"ls -d /proc/sys/fs/binfmt_misc/rosetta"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(procSession).To(Exit(0))
|
||||
Expect(procSession.outputToString()).To(ContainSubstring("/proc/sys/fs/binfmt_misc/rosetta"))
|
||||
|
||||
proc2 := sshMachine{}
|
||||
proc2Session, err := mb.setName(name).setCmd(proc2.withSSHCommand([]string{"ls -d /proc/sys/fs/binfmt_misc/qemu-x86_64"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(proc2Session.ExitCode()).To(Equal(2))
|
||||
})
|
||||
|
||||
It("machine init with rosetta=false", func() {
|
||||
skipIfVmtype(define.QemuVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.WSLVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.HyperVVirt, "Test is only for AppleHv")
|
||||
skipIfVmtype(define.LibKrun, "Test is only for AppleHv")
|
||||
if runtime.GOARCH != "arm64" {
|
||||
Skip("Test is only for AppleHv with arm64 architecture")
|
||||
}
|
||||
configDir := filepath.Join(testDir, ".config", "containers")
|
||||
err := os.MkdirAll(configDir, 0755)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(filepath.Join(configDir, "containers.conf"), rosettaConfig, 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
i := initMachine{}
|
||||
name := randomString()
|
||||
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath)).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(session).To(Exit(0))
|
||||
|
||||
s := startMachine{}
|
||||
ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ssession).Should(Exit(0))
|
||||
|
||||
inspect := new(inspectMachine)
|
||||
inspect = inspect.withFormat("{{.Rosetta}}")
|
||||
inspectSession, err := mb.setName(name).setCmd(inspect).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(inspectSession).To(Exit(0))
|
||||
Expect(inspectSession.outputToString()).To(Equal("false"))
|
||||
|
||||
mnt := sshMachine{}
|
||||
mntSession, err := mb.setName(name).setCmd(mnt.withSSHCommand([]string{"ls -d /mnt/rosetta"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mntSession.ExitCode()).To(Equal(2))
|
||||
|
||||
proc := sshMachine{}
|
||||
procSession, err := mb.setName(name).setCmd(proc.withSSHCommand([]string{"ls -d /proc/sys/fs/binfmt_misc/rosetta"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(procSession.ExitCode()).To(Equal(2))
|
||||
|
||||
proc2 := sshMachine{}
|
||||
proc2Session, err := mb.setName(name).setCmd(proc2.withSSHCommand([]string{"ls -d /proc/sys/fs/binfmt_misc/qemu-x86_64"})).run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(proc2Session.outputToString()).To(ContainSubstring("/proc/sys/fs/binfmt_misc/qemu-x86_64"))
|
||||
})
|
||||
})
|
||||
|
||||
var p4Config = []byte(`{
|
||||
@ -455,3 +549,8 @@ var p4Config = []byte(`{
|
||||
"LastUp": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
`)
|
||||
|
||||
var rosettaConfig = []byte(`
|
||||
[machine]
|
||||
rosetta=false
|
||||
`)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
@ -62,7 +63,13 @@ var _ = BeforeSuite(func() {
|
||||
if pullError != nil {
|
||||
Fail(fmt.Sprintf("failed to pull wsl disk: %q", pullError))
|
||||
}
|
||||
|
||||
if testProvider.VMType() == define.AppleHvVirt {
|
||||
cmd := exec.Command("softwareupdate", "--install-rosetta", "--agree-to-license")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
Fail(fmt.Sprintf("Command failed with error: %q", err))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var _ = SynchronizedAfterSuite(func() {}, func() {})
|
||||
|
@ -557,3 +557,7 @@ func createNetworkUnit(netPort uint64) (string, error) {
|
||||
netUnit.Add("Install", "WantedBy", "multi-user.target")
|
||||
return netUnit.ToString()
|
||||
}
|
||||
|
||||
func (h HyperVStubber) GetRosetta(mc *vmconfigs.MachineConfig) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/machine/define"
|
||||
"github.com/containers/podman/v5/pkg/systemd/parser"
|
||||
@ -65,6 +66,7 @@ type DynamicIgnition struct {
|
||||
Cfg Config
|
||||
Rootful bool
|
||||
NetRecover bool
|
||||
Rosetta bool
|
||||
}
|
||||
|
||||
func (ign *DynamicIgnition) Write() error {
|
||||
@ -239,6 +241,19 @@ func (ign *DynamicIgnition) GenerateIgnitionConfig() error {
|
||||
ignSystemd.Units = append(ignSystemd.Units, qemuUnit)
|
||||
}
|
||||
|
||||
// Only AppleHv with Apple Silicon can use Rosetta
|
||||
if ign.VMType == define.AppleHvVirt && runtime.GOARCH == "arm64" {
|
||||
rosettaUnit := Systemd{
|
||||
Units: []Unit{
|
||||
{
|
||||
Enabled: BoolToPtr(true),
|
||||
Name: "rosetta-activation.service",
|
||||
},
|
||||
},
|
||||
}
|
||||
ignSystemd.Units = append(ignSystemd.Units, rosettaUnit.Units...)
|
||||
}
|
||||
|
||||
// Only after all checks are done
|
||||
// it's ready create the ingConfig
|
||||
ign.Cfg = Config{
|
||||
|
@ -139,3 +139,7 @@ func (l LibKrunStubber) RequireExclusiveActive() bool {
|
||||
func (l LibKrunStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LibKrunStubber) GetRosetta(mc *vmconfigs.MachineConfig) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -360,3 +360,7 @@ func (q *QEMUStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error
|
||||
func (q *QEMUStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, q.VMType(), mc.Name)
|
||||
}
|
||||
|
||||
func (q *QEMUStubber) GetRosetta(mc *vmconfigs.MachineConfig) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ type MachineConfig struct {
|
||||
|
||||
// Starting is defined as "on" but not fully booted
|
||||
Starting bool
|
||||
|
||||
Rosetta bool
|
||||
}
|
||||
|
||||
type machineImage interface { //nolint:unused
|
||||
@ -99,6 +101,7 @@ type VMProvider interface { //nolint:interfacebloat
|
||||
UseProviderNetworkSetup() bool
|
||||
RequireExclusiveActive() bool
|
||||
UpdateSSHPort(mc *MachineConfig, port int) error
|
||||
GetRosetta(mc *MachineConfig) (bool, error)
|
||||
}
|
||||
|
||||
// HostUser describes the host user
|
||||
|
@ -342,3 +342,7 @@ func (w WSLStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *
|
||||
// pull if needed and decompress to image location
|
||||
return myDisk.Get()
|
||||
}
|
||||
|
||||
func (w WSLStubber) GetRosetta(mc *vmconfigs.MachineConfig) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user