Skip to content

Commit

Permalink
Merge pull request #21670 from tnk4on/rosetta-support
Browse files Browse the repository at this point in the history
applehv: Rosetta support
  • Loading branch information
openshift-merge-bot[bot] authored May 17, 2024
2 parents cb658b0 + fe7cc67 commit 34d2d20
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 3 deletions.
6 changes: 6 additions & 0 deletions cmd/podman/machine/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/source/markdown/podman-machine-inspect.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
12 changes: 12 additions & 0 deletions pkg/machine/apple/vfkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions pkg/machine/apple/vfkit/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@ type Helper struct {
Endpoint string
BinaryPath *define.VMFile
VirtualMachine *config.VirtualMachine
Rosetta bool
}
29 changes: 29 additions & 0 deletions pkg/machine/applehv/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
}
6 changes: 4 additions & 2 deletions pkg/machine/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ type SSHOptions struct {
}

type StartOptions struct {
NoInfo bool
Quiet bool
NoInfo bool
Quiet bool
Rosetta bool
}

type StopOptions struct{}
Expand Down Expand Up @@ -117,6 +118,7 @@ type InspectInfo struct {
State define.Status
UserModeNetworking bool
Rootful bool
Rosetta bool
}

// ImageConfig describes the bootable image for the VM
Expand Down
4 changes: 4 additions & 0 deletions pkg/machine/define/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
99 changes: 99 additions & 0 deletions pkg/machine/e2e/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(`{
Expand Down Expand Up @@ -455,3 +549,8 @@ var p4Config = []byte(`{
"LastUp": "0001-01-01T00:00:00Z"
}
`)

var rosettaConfig = []byte(`
[machine]
rosetta=false
`)
9 changes: 8 additions & 1 deletion pkg/machine/e2e/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -69,7 +70,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() {})
Expand Down
4 changes: 4 additions & 0 deletions pkg/machine/hyperv/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
15 changes: 15 additions & 0 deletions pkg/machine/ignition/ignition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -65,6 +66,7 @@ type DynamicIgnition struct {
Cfg Config
Rootful bool
NetRecover bool
Rosetta bool
}

func (ign *DynamicIgnition) Write() error {
Expand Down Expand Up @@ -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{
Expand Down
4 changes: 4 additions & 0 deletions pkg/machine/libkrun/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions pkg/machine/qemu/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 3 additions & 0 deletions pkg/machine/vmconfigs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/machine/wsl/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

1 comment on commit 34d2d20

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

Please sign in to comment.