From 467ad874de9cbccb03118ef5baec703bacee7e51 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 26 Oct 2023 20:42:58 +0900 Subject: [PATCH] Use VZ by default for new instances on macOS >= 13.5 VZ is now the default type for new instances >= 13.5, unless the config is incompatible with vz (e.g., `firmware.legacyBIOS=true`, `mountType=9p`). Existing instances will continue to use QEMU by default, unless `vz-identifier` is present in the instance directory. Run `limactl start` with `--debug` to see how the vmType is resolved. Signed-off-by: Akihiro Suda --- .github/workflows/test.yml | 6 +- examples/default.yaml | 2 +- pkg/limayaml/defaults.go | 100 ++++++++++++++++-- pkg/limayaml/defaults_test.go | 8 +- .../content/en/docs/Config/VMType/_index.md | 5 +- 5 files changed, 105 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8dd189d5e1e6..33f2d1d181e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -138,6 +138,7 @@ jobs: integration: name: Integration tests + # on macOS 12, the default vmType is QEMU runs-on: macos-12 timeout-minutes: 120 steps: @@ -407,13 +408,14 @@ jobs: vz: name: "vz" + # on macOS 13, the default vmType is VZ runs-on: macos-13 timeout-minutes: 120 strategy: fail-fast: false matrix: template: - - experimental/vz.yaml + - default.yaml - fedora.yaml steps: - uses: actions/checkout@v4 @@ -437,6 +439,4 @@ jobs: - name: Uninstall qemu run: brew uninstall --ignore-dependencies --force qemu - name: Test - env: - LIMACTL_CREATE_ARGS: "--vm-type vz --mount-type virtiofs --rosetta --network vzNAT" run: ./hack/test-templates.sh templates/${{ matrix.template }} diff --git a/examples/default.yaml b/examples/default.yaml index aeaac6dd7181..c6e2c1b6aa0d 100644 --- a/examples/default.yaml +++ b/examples/default.yaml @@ -8,7 +8,7 @@ # VM type: "qemu" or "vz" (on macOS 13 and later). # The vmType can be specified only on creating the instance. # The vmType of existing instances cannot be changed. -# 🟢 Builtin default: "qemu" +# 🟢 Builtin default: "vz" (on macOS 13.5 and later), "qemu" (on others) vmType: null # OS: "Linux". diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index 0cf38bc90e66..b37d9b9fa851 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -3,6 +3,7 @@ package limayaml import ( "bytes" "crypto/sha256" + "errors" "fmt" "net" "os" @@ -11,6 +12,7 @@ import ( "strconv" "text/template" + "github.com/coreos/go-semver/semver" "github.com/docker/go-units" "github.com/lima-vm/lima/pkg/networks" "github.com/pbnjay/memory" @@ -127,13 +129,7 @@ func defaultGuestInstallPrefix() string { // - DNS are picked from the highest priority where DNS is not empty. // - CACertificates Files and Certs are uniquely appended in d, y, o order func FillDefault(y, d, o *LimaYAML, filePath string) { - if y.VMType == nil { - y.VMType = d.VMType - } - if o.VMType != nil { - y.VMType = o.VMType - } - y.VMType = pointer.String(ResolveVMType(y.VMType)) + y.VMType = pointer.String(ResolveVMType(y, d, o, filePath)) if y.OS == nil { y.OS = d.OS } @@ -860,11 +856,95 @@ func NewVMType(driver string) VMType { } } -func ResolveVMType(s *string) VMType { - if s == nil || *s == "" || *s == "default" { +func isExistingInstanceDir(dir string) bool { + // existence of "lima.yaml" does not signify existence of the instance, + // because the file is created during the initialization of the instance. + for _, f := range []string{filenames.HostAgentStdoutLog, filenames.HostAgentStderrLog, + filenames.VzIdentifier, filenames.BaseDisk, filenames.DiffDisk, filenames.CIDataISO} { + file := filepath.Join(dir, f) + if _, err := os.Lstat(file); !errors.Is(err, os.ErrNotExist) { + return true + } + } + return false +} + +func ResolveVMType(y, d, o *LimaYAML, filePath string) VMType { + // Check if the VMType is explicitly specified + for i, f := range []*LimaYAML{o, y, d} { + if f.VMType != nil && *f.VMType != "" && *f.VMType != "default" { + logrus.Debugf("ResolveVMType: resolved VMType %q (explicitly specified in []*LimaYAML{o,y,d}[%d])", *f.VMType, i) + return NewVMType(*f.VMType) + } + } + + // If this is an existing instance, guess the VMType from the contents of the instance directory. + // Note that the instance directory may be created by a previous version of Lima. + if dir, basename := filepath.Split(filePath); dir != "" && basename == filenames.LimaYAML && isExistingInstanceDir(dir) { + vzIdentifier := filepath.Join(dir, filenames.VzIdentifier) + if _, err := os.Lstat(vzIdentifier); !errors.Is(err, os.ErrNotExist) { + logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance, with %q)", VZ, vzIdentifier) + return VZ + } + logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance, without %q)", QEMU, vzIdentifier) + return QEMU + } + + // Resolve the best type, depending on GOOS + switch runtime.GOOS { + case "darwin": + macOSProductVersion, err := osutil.MacOSProductVersion() + if err != nil { + logrus.WithError(err).Warn("Failed to get macOS product version") + logrus.Debugf("ResolveVMType: resolved VMType %q (default for unknown version of macOS)", QEMU) + return QEMU + } + // Virtualization.framework in macOS prior to 13.5 could not boot Linux kernel v6.2 on Intel + // https://github.com/lima-vm/lima/issues/1577 + if macOSProductVersion.LessThan(*semver.New("13.5.0")) { + logrus.Debugf("ResolveVMType: resolved VMType %q (default for macOS prior to 13.5)", QEMU) + return QEMU + } + // Use QEMU if the config depends on QEMU + for i, f := range []*LimaYAML{o, y, d} { + if f.Arch != nil && !IsNativeArch(*f.Arch) { + logrus.Debugf("ResolveVMType: resolved VMType %q (non-native arch=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Arch, i) + return QEMU + } + if f.Firmware.LegacyBIOS != nil && *f.Firmware.LegacyBIOS { + logrus.Debugf("ResolveVMType: resolved VMType %q (firmware.legacyBIOS is specified in []*LimaYAML{o,y,d}[%d])", QEMU, i) + return QEMU + } + if f.MountType != nil && *f.MountType == NINEP { + logrus.Debugf("ResolveVMType: resolved VMType %q (mountType=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, NINEP, i) + return QEMU + } + if f.Audio.Device != nil { + switch *f.Audio.Device { + case "", "none", "default", "vz": + // NOP + default: + logrus.Debugf("ResolveVMType: resolved VMType %q (audio.device=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Audio.Device, i) + return QEMU + } + } + if f.Video.Display != nil { + switch *f.Video.Display { + case "", "none", "default", "vz": + // NOP + default: + logrus.Debugf("ResolveVMType: resolved VMType %q (video.display=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Video.Display, i) + return QEMU + } + } + } + // Use VZ if the config is compatible with VZ + logrus.Debugf("ResolveVMType: resolved VMType %q (default for macOS 13.5 and later)", VZ) + return VZ + default: + logrus.Debugf("ResolveVMType: resolved VMType %q (default for GOOS=%q)", QEMU, runtime.GOOS) return QEMU } - return NewVMType(*s) } func ResolveOS(s *string) OS { diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go index c2a326dc881e..9e9558709645 100644 --- a/pkg/limayaml/defaults_test.go +++ b/pkg/limayaml/defaults_test.go @@ -14,13 +14,17 @@ import ( "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" + "github.com/sirupsen/logrus" "github.com/xorcare/pointer" "gotest.tools/v3/assert" ) func TestFillDefault(t *testing.T) { + logrus.SetLevel(logrus.DebugLevel) var d, y, o LimaYAML + defaultVMType := ResolveVMType(&y, &d, &o, "") + opts := []cmp.Option{ // Consider nil slices and empty slices to be identical cmpopts.EquateEmpty(), @@ -60,7 +64,7 @@ func TestFillDefault(t *testing.T) { // Builtin default values builtin := LimaYAML{ - VMType: pointer.String("qemu"), + VMType: &defaultVMType, OS: pointer.String(LINUX), Arch: pointer.String(arch), CPUType: map[Arch]string{ @@ -184,6 +188,7 @@ func TestFillDefault(t *testing.T) { } expect := builtin + expect.VMType = pointer.String(QEMU) // due to NINEP expect.HostResolver.Hosts = map[string]string{ "MY.Host": "host.lima.internal", } @@ -439,6 +444,7 @@ func TestFillDefault(t *testing.T) { // "TWO" does not exist in filledDefaults.Env, so is set from d.Env expect.Env["TWO"] = d.Env["TWO"] + t.Logf("d.vmType=%q, y.vmType=%q, expect.vmType=%q", *d.VMType, *y.VMType, *expect.VMType) FillDefault(&y, &d, &LimaYAML{}, filePath) assert.DeepEqual(t, &y, &expect, opts...) diff --git a/website/content/en/docs/Config/VMType/_index.md b/website/content/en/docs/Config/VMType/_index.md index cb6a2bc16665..4bcc74248d24 100644 --- a/website/content/en/docs/Config/VMType/_index.md +++ b/website/content/en/docs/Config/VMType/_index.md @@ -22,9 +22,12 @@ flowchart intel_on_arm -- "No" --> vz["VZ"] ``` +The default vmType is QEMU in Lima prior to v1.0. +Starting with Lima v1.0, Lima will use VZ by default on macOS (>= 13.5) for new instances, +unless the config is incompatible with VZ. + ## QEMU "qemu" option makes use of QEMU to run guest operating system. -This option is used by default if "vmType" is not set. ## VZ > **Warning**