From b0da6e347a9c6f5e10a4418de245f17cda8fed1c Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Tue, 21 Feb 2023 12:53:01 -0500 Subject: [PATCH 01/19] feat: enable Virtualization.framework and Rosetta Signed-off-by: Justin Alvarez --- .gitmodules | 3 +- Makefile | 5 +- cmd/finch/main.go | 4 +- cmd/finch/virtual_machine_init.go | 2 +- cmd/finch/virtual_machine_init_test.go | 8 +- cmd/finch/virtual_machine_start.go | 2 +- cmd/finch/virtual_machine_start_test.go | 8 +- deps/finch-core | 2 +- e2e/vm/config_test.go | 62 +++++ finch.yaml | 14 +- pkg/config/config.go | 14 +- pkg/config/config_test.go | 35 ++- pkg/config/defaults.go | 8 + pkg/config/defaults_test.go | 24 +- pkg/config/lima_config_applier.go | 134 +++++++++- pkg/config/lima_config_applier_test.go | 231 +++++++++++++++--- pkg/dependency/vmnet/vmnet_test.go | 1 - pkg/disk/disk.go | 105 ++++++-- pkg/disk/disk_test.go | 107 ++++++-- pkg/mocks/pkg_config_lima_config_applier.go | 8 +- ..._config_lima_config_applier_system_deps.go | 65 +++++ pkg/system/stdlib.go | 8 + pkg/system/system.go | 10 + 23 files changed, 750 insertions(+), 110 deletions(-) create mode 100644 pkg/mocks/pkg_config_lima_config_applier_system_deps.go diff --git a/.gitmodules b/.gitmodules index 5d08fcc22..6dd336705 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "deps/finch-core"] path = deps/finch-core - url = https://github.com/runfinch/finch-core.git + url = https://github.com/pendo324/finch-core.git + branch = lima-with-vz-extra-disk diff --git a/Makefile b/Makefile index ca62f45ea..1fe2730c7 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ arch-test: @if [ $(SUPPORTED_ARCH) != "true" ]; then echo "Unsupported architecture: $(ARCH)"; exit "1"; fi .PHONY: all -all: arch-test finch finch-core finch.yaml networks.yaml config.yaml lima-and-qemu +all: arch-test finch networks.yaml config.yaml lima-and-qemu finch-core finch.yaml .PHONY: finch-core finch-core: @@ -62,10 +62,11 @@ finch-core: FINCH_OS_x86_URL="$(FINCH_OS_x86_URL)" \ FINCH_OS_AARCH64_URL="$(FINCH_OS_AARCH64_URL)" \ VDE_TEMP_PREFIX=$(CORE_VDE_PREFIX) \ - $(MAKE) + $(MAKE) all lima mkdir -p _output cd deps/finch-core/_output && tar c * | tar Cvx $(OUTDIR) + cd deps/finch-core/src/lima/_output && tar c * | tar Cvx $(OUTDIR)/lima rm -rf $(OUTDIR)/lima-template .PHONY: lima-and-qemu diff --git a/cmd/finch/main.go b/cmd/finch/main.go index a93cf811b..2685181f9 100644 --- a/cmd/finch/main.go +++ b/cmd/finch/main.go @@ -116,11 +116,11 @@ func virtualMachineCommands( lcc, logger, optionalDepGroups, - config.NewLimaApplier(fc, fs, fp.LimaOverrideConfigPath()), + config.NewLimaApplier(fc, ecc, fs, fp.LimaOverrideConfigPath(), system.NewStdLib()), config.NewNerdctlApplier(fssh.NewDialer(), fs, fp.LimaSSHPrivateKeyPath()), fp, fs, - disk.NewUserDataDiskManager(lcc, &afero.OsFs{}, fp, system.NewStdLib().Env("HOME")), + disk.NewUserDataDiskManager(lcc, ecc, &afero.OsFs{}, fp, system.NewStdLib().Env("HOME"), fc), ) } diff --git a/cmd/finch/virtual_machine_init.go b/cmd/finch/virtual_machine_init.go index 0c691b864..ab7161562 100644 --- a/cmd/finch/virtual_machine_init.go +++ b/cmd/finch/virtual_machine_init.go @@ -81,7 +81,7 @@ func (iva *initVMAction) run() error { iva.logger.Errorf("Dependency error: %v", err) } - err = iva.limaConfigApplier.Apply() + err = iva.limaConfigApplier.Apply(true) if err != nil { return err } diff --git a/cmd/finch/virtual_machine_init_test.go b/cmd/finch/virtual_machine_init_test.go index cf73bf1f5..38a060ab3 100644 --- a/cmd/finch/virtual_machine_init_test.go +++ b/cmd/finch/virtual_machine_init_test.go @@ -71,7 +71,7 @@ func TestInitVMAction_runAdapter(t *testing.T) { logger.EXPECT().Debugf("Status of virtual machine: %s", "") command := mocks.NewCommand(ctrl) - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(true).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) lcc.EXPECT().CreateWithoutStdio("start", fmt.Sprintf("--name=%s", limaInstanceName), mockBaseYamlFilePath, "--tty=false").Return(command) @@ -135,7 +135,7 @@ func TestInitVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte(""), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "") - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(true).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) command := mocks.NewCommand(ctrl) @@ -253,7 +253,7 @@ func TestInitVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte(""), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "") - lca.EXPECT().Apply().Return(errors.New("load config fails")) + lca.EXPECT().Apply(true).Return(errors.New("load config fails")) logger.EXPECT().Errorf("Dependency error: %v", fmt.Errorf("failed to install dependencies: %w", errors.Join(fmt.Errorf("%s: %w", "mock_error_msg", errors.Join(errors.New("dependency error occurs")))), @@ -279,7 +279,7 @@ func TestInitVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte(""), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "") - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(true).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) logs := []byte("stdout + stderr") diff --git a/cmd/finch/virtual_machine_start.go b/cmd/finch/virtual_machine_start.go index 0edb893f6..2ede0b464 100644 --- a/cmd/finch/virtual_machine_start.go +++ b/cmd/finch/virtual_machine_start.go @@ -74,7 +74,7 @@ func (sva *startVMAction) run() error { sva.logger.Errorf("Dependency error: %v", err) } - err = sva.limaConfigApplier.Apply() + err = sva.limaConfigApplier.Apply(false) if err != nil { return err } diff --git a/cmd/finch/virtual_machine_start_test.go b/cmd/finch/virtual_machine_start_test.go index 1b65543c2..7bdb39691 100644 --- a/cmd/finch/virtual_machine_start_test.go +++ b/cmd/finch/virtual_machine_start_test.go @@ -70,7 +70,7 @@ func TestStartVMAction_runAdapter(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(false).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) @@ -145,7 +145,7 @@ func TestStartVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(false).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) @@ -262,7 +262,7 @@ func TestStartVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") - lca.EXPECT().Apply().Return(errors.New("load config fails")) + lca.EXPECT().Apply(false).Return(errors.New("load config fails")) logger.EXPECT().Errorf("Dependency error: %v", fmt.Errorf("failed to install dependencies: %w", @@ -295,7 +295,7 @@ func TestStartVMAction_run(t *testing.T) { getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") - lca.EXPECT().Apply().Return(nil) + lca.EXPECT().Apply(false).Return(nil) dm.EXPECT().EnsureUserDataDisk().Return(nil) diff --git a/deps/finch-core b/deps/finch-core index 31067f9e3..fa1a20e17 160000 --- a/deps/finch-core +++ b/deps/finch-core @@ -1 +1 @@ -Subproject commit 31067f9e328bb05543c547b3aad728a31db236f9 +Subproject commit fa1a20e17e0f670ff9d34c2508a660f4d15ba91b diff --git a/e2e/vm/config_test.go b/e2e/vm/config_test.go index 8f8a8bf6e..1afdebb04 100644 --- a/e2e/vm/config_test.go +++ b/e2e/vm/config_test.go @@ -166,5 +166,67 @@ additional_directories: gomega.Expect(limaCfg.Mounts[1].Location).Should(gomega.Equal("/tmp/workspace")) gomega.Expect(limaCfg.Mounts[1].Writable).Should(gomega.Equal(pointer.Bool(true))) }) + + ginkgo.It("does not update init-only config values when values are changed between start/stop", func() { + startCmdSession := updateAndApplyConfig(o, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) + gomega.Expect(startCmdSession).Should(gexec.Exit(0)) + + gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) + cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(*limaCfg.CPUs).Should(gomega.Equal(6)) + gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("4GiB")) + gomega.Expect(*limaCfg.VMType).Should(gomega.Equal("qemu")) + gomega.Expect(limaCfg.Rosetta.Enabled).Should(gomega.Equal(false)) + gomega.Expect(limaCfg.Rosetta.BinFmt).Should(gomega.Equal(false)) + }) + }) + + ginkgo.Describe("Config (after init)", ginkgo.Serial, func() { + var limaConfigFilePath string + + ginkgo.BeforeEach(func() { + origFinchCfg := readFile(finchConfigFilePath) + limaConfigFilePath = defaultLimaConfigFilePath + if installed { + path, err := exec.LookPath(e2e.InstalledTestSubject) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + realFinchPath, err := filepath.EvalSymlinks(path) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + } + origLimaCfg := readFile(limaConfigFilePath) + + command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + + ginkgo.DeferCleanup(func() { + writeFile(finchConfigFilePath, origFinchCfg) + writeFile(limaConfigFilePath, origLimaCfg) + }) + }) + + ginkgo.It("updates init-only config values when values are changed after init", func() { + writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) + initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() + gomega.Expect(initCmdSession).Should(gexec.Exit(0)) + + gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) + cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(*limaCfg.CPUs).Should(gomega.Equal(6)) + gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("4GiB")) + gomega.Expect(*limaCfg.VMType).Should(gomega.Equal("vz")) + gomega.Expect(limaCfg.Rosetta.Enabled).Should(gomega.Equal(false)) + gomega.Expect(limaCfg.Rosetta.BinFmt).Should(gomega.Equal(true)) + }) }) } diff --git a/finch.yaml b/finch.yaml index 46276ca4b..f1dcc0f43 100644 --- a/finch.yaml +++ b/finch.yaml @@ -77,10 +77,6 @@ mounts: # 🔵 This file: true (only for "/tmp/lima") writable: true -# Mount type for above mounts, such as "reverse-sshfs" (from sshocker) or "9p" (EXPERIMENTAL, from QEMU’s virtio-9p-pci, aka virtfs) -# 🟢 Builtin default: "reverse-sshfs" -mountType: reverse-sshfs - # Lima disks to attach to the instance. The disks will be accessible from inside the # instance, labeled by name. (e.g. if the disk is named "data", it will be labeled # "lima-data" inside the instance). The disk will be mounted inside the instance at @@ -134,16 +130,14 @@ containerd: # multiple times, e.g. when the host VM is being restarted. # 🟢 Builtin default: null provision: -# Install packages needed for QEMU user-mode emulation -- mode: system - script: | - #!/bin/bash - dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 - mode: boot script: | systemctl stop NetworkManager-wait-online.service systemctl reset-failed NetworkManager-wait-online.service systemctl mask NetworkManager-wait-online.service +- mode: boot + script: | + modprobe virtiofs # # `user` is executed without the root privilege - mode: user script: | @@ -236,4 +230,4 @@ env: # Containerd namespace is used by the lima cidata script # 40-install-containerd.sh. Specifically this variable is defining the # Buildkit Workers Containerd namespace. - CONTAINERD_NAMESPACE: finch \ No newline at end of file + CONTAINERD_NAMESPACE: finch diff --git a/pkg/config/config.go b/pkg/config/config.go index 2eaaac9b8..f1a436199 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -14,6 +14,7 @@ import ( "fmt" "path" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/spf13/afero" "gopkg.in/yaml.v3" @@ -35,6 +36,17 @@ type Finch struct { // For example, if you want to mount a directory into a container, and that directory is not under your home directory, // then you'll need to specify this field to add that directory or any ascendant of it as a work directory. AdditionalDirectories []AdditionalDirectory `yaml:"additional_directories,omitempty"` + // VMType sets which technology to use for Finch's VM. + // Currently supports `qemu` and `vz` (Virtualization.framework). + // Also sets mountType to "virtiofs", instead of the default "reverse-sshfs" + // Requires macOS macOS 11.0 or later. + // This setting will only be applied on vm init. + VMType *limayaml.VMType `yaml:"vmType,omitempty"` + // Use Rosetta 2 when available. Forces vmType to "vz" (Virtualization.framework) if set to `true`. + // Requires macOS 13.0 or later and an Apple Silicon (ARM64) mac. + // Has no effect on systems where Rosetta 2 is not available (Intel/AMD64 macs, or macOS < 13.0). + // This setting will only be applied on vm init. + Rosetta *bool `yaml:"rosetta,omitempty"` } // Nerdctl is a copy from github.com/containerd/nerdctl/cmd/nerdctl/main.go @@ -58,7 +70,7 @@ type Nerdctl struct { // //go:generate mockgen -copyright_file=../../copyright_header -destination=../mocks/pkg_config_lima_config_applier.go -package=mocks -mock_names LimaConfigApplier=LimaConfigApplier . LimaConfigApplier type LimaConfigApplier interface { - Apply() error + Apply(isInit bool) error } // NerdctlConfigApplier applies nerdctl configuration changes. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7a5be8f42..e6a9439a8 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -41,7 +41,12 @@ cpus: 8 // 12_884_901_888 == 12GiB mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)) }, - want: &Finch{Memory: pointer.String("4GiB"), CPUs: pointer.Int(8)}, + want: &Finch{ + Memory: pointer.String("4GiB"), + CPUs: pointer.Int(8), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, wantErr: nil, }, { @@ -52,7 +57,12 @@ cpus: 8 deps.EXPECT().NumCPU().Return(4).Times(2) mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)).Times(2) }, - want: &Finch{Memory: pointer.String("3GiB"), CPUs: pointer.Int(2)}, + want: &Finch{ + Memory: pointer.String("3GiB"), + CPUs: pointer.Int(2), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, wantErr: nil, }, { @@ -63,7 +73,12 @@ cpus: 8 deps.EXPECT().NumCPU().Return(4).Times(2) mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)).Times(1) }, - want: &Finch{Memory: pointer.String("2GiB"), CPUs: pointer.Int(2)}, + want: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(2), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, wantErr: nil, }, { @@ -74,7 +89,12 @@ cpus: 8 deps.EXPECT().NumCPU().Return(4).Times(2) mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)).Times(2) }, - want: &Finch{Memory: pointer.String("3GiB"), CPUs: pointer.Int(2)}, + want: &Finch{ + Memory: pointer.String("3GiB"), + CPUs: pointer.Int(2), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, wantErr: nil, }, { @@ -85,7 +105,12 @@ cpus: 8 deps.EXPECT().NumCPU().Return(4).Times(1) mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)).Times(1) }, - want: &Finch{Memory: pointer.String("3GiB"), CPUs: pointer.Int(2)}, + want: &Finch{ + Memory: pointer.String("3GiB"), + CPUs: pointer.Int(2), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, wantErr: nil, }, { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index c9ac9bfdb..66c0a9c92 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -38,5 +38,13 @@ func applyDefaults(cfg *Finch, deps LoadSystemDeps, mem fmemory.Memory) *Finch { } } + if cfg.VMType == nil { + cfg.VMType = pointer.String("qemu") + } + + if cfg.Rosetta == nil { + cfg.Rosetta = pointer.Bool(false) + } + return cfg } diff --git a/pkg/config/defaults_test.go b/pkg/config/defaults_test.go index 2500399d5..23235f96a 100644 --- a/pkg/config/defaults_test.go +++ b/pkg/config/defaults_test.go @@ -31,8 +31,10 @@ func Test_applyDefaults(t *testing.T) { mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)) }, want: &Finch{ - CPUs: pointer.Int(2), - Memory: pointer.String("3GiB"), + CPUs: pointer.Int(2), + Memory: pointer.String("3GiB"), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, }, { @@ -44,8 +46,10 @@ func Test_applyDefaults(t *testing.T) { deps.EXPECT().NumCPU().Return(8) }, want: &Finch{ - CPUs: pointer.Int(2), - Memory: pointer.String("4GiB"), + CPUs: pointer.Int(2), + Memory: pointer.String("4GiB"), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, }, { @@ -58,8 +62,10 @@ func Test_applyDefaults(t *testing.T) { mem.EXPECT().TotalMemory().Return(uint64(12_884_901_888)) }, want: &Finch{ - CPUs: pointer.Int(6), - Memory: pointer.String("3GiB"), + CPUs: pointer.Int(6), + Memory: pointer.String("3GiB"), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, }, { @@ -71,8 +77,10 @@ func Test_applyDefaults(t *testing.T) { mem.EXPECT().TotalMemory().Return(uint64(1_073_741_824)) }, want: &Finch{ - CPUs: pointer.Int(2), - Memory: pointer.String("2GiB"), + CPUs: pointer.Int(2), + Memory: pointer.String("2GiB"), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, }, } diff --git a/pkg/config/lima_config_applier.go b/pkg/config/lima_config_applier.go index 7180ea0c7..002ae5fe3 100644 --- a/pkg/config/lima_config_applier.go +++ b/pkg/config/lima_config_applier.go @@ -5,33 +5,60 @@ package config import ( "fmt" + "strconv" + "strings" "github.com/lima-vm/lima/pkg/limayaml" + "github.com/runfinch/finch/pkg/command" + "github.com/runfinch/finch/pkg/system" "github.com/spf13/afero" "github.com/xorcare/pointer" "gopkg.in/yaml.v3" ) +const USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER = "# cross-arch tools" + +// LoadSystemDeps contains the system dependencies for Load. +// +//go:generate mockgen -copyright_file=../../copyright_header -destination=../mocks/pkg_config_lima_config_applier_system_deps.go -package=mocks -mock_names LimaConfigApplierSystemDeps=LimaConfigApplierSystemDeps . LimaConfigApplierSystemDeps +type LimaConfigApplierSystemDeps interface { + system.RuntimeArchGetter + system.RuntimeOSGetter +} + type limaConfigApplier struct { cfg *Finch + cmdCreator command.Creator fs afero.Fs limaConfigPath string + systemDeps LimaConfigApplierSystemDeps } var _ LimaConfigApplier = (*limaConfigApplier)(nil) // NewLimaApplier creates a new LimaConfigApplier that // applies lima configuration changes by writing to the lima config file on the disk. -func NewLimaApplier(cfg *Finch, fs afero.Fs, limaConfigPath string) LimaConfigApplier { +func NewLimaApplier(cfg *Finch, cmdCreator command.Creator, fs afero.Fs, limaConfigPath string, systemDeps LimaConfigApplierSystemDeps) LimaConfigApplier { return &limaConfigApplier{ cfg: cfg, + cmdCreator: cmdCreator, fs: fs, limaConfigPath: limaConfigPath, + systemDeps: systemDeps, } } -// Apply reads the Finch config from disk and writes the Lima-related portion to the lima config file. -func (lca *limaConfigApplier) Apply() error { +// Apply writes Lima-specific config values from Finch's config to the supplied lima config file path. +// Apply will create a lima config file at the path if it does not exist. +func (lca *limaConfigApplier) Apply(isInit bool) error { + if cfgExists, err := afero.Exists(lca.fs, lca.limaConfigPath); err != nil { + return fmt.Errorf("error checking if file at path %s exists, error: %w", lca.limaConfigPath, err) + } else if !cfgExists { + if err := afero.WriteFile(lca.fs, lca.limaConfigPath, []byte(""), 0o644); err != nil { + return fmt.Errorf("failed to create the an empty lima config file: %w", err) + } + } + b, err := afero.ReadFile(lca.fs, lca.limaConfigPath) if err != nil { return fmt.Errorf("failed to load the lima config file: %w", err) @@ -51,6 +78,14 @@ func (lca *limaConfigApplier) Apply() error { }) } + if isInit { + cfgAfterInit, err := lca.applyInit(&limaCfg) + if err != nil { + return fmt.Errorf("failed to apply init-only config values: %w", err) + } + limaCfg = *cfgAfterInit + } + limaCfgBytes, err := yaml.Marshal(limaCfg) if err != nil { return fmt.Errorf("failed to marshal the lima config file: %w", err) @@ -62,3 +97,96 @@ func (lca *limaConfigApplier) Apply() error { return nil } + +// applyInit changes settings that will only apply to the VM after a new init. +func (lca *limaConfigApplier) applyInit(limaCfg *limayaml.LimaYAML) (*limayaml.LimaYAML, error) { + hasSupport, hasSupportErr := lca.supportsVirtualizationFramework() + if *lca.cfg.Rosetta == true && + lca.systemDeps.OS() == "darwin" && + lca.systemDeps.Arch() == "arm64" { + + if hasSupportErr != nil { + return nil, fmt.Errorf("failed to check for virtualization framework support: %w", hasSupportErr) + } + if hasSupport == false { + return nil, fmt.Errorf(`system does not have virtualization framework support, change vmType to "qemu"`) + } + + limaCfg.Rosetta.Enabled = true + limaCfg.Rosetta.BinFmt = true + limaCfg.VMType = pointer.String("vz") + limaCfg.MountType = pointer.String("virtiofs") + toggleUserModeEmulationInstallationScript(limaCfg, false) + } else { + if *lca.cfg.VMType == "vz" { + if hasSupportErr != nil { + return nil, fmt.Errorf("failed to check for virtualization framework support: %w", hasSupportErr) + } + if !hasSupport { + return nil, fmt.Errorf(`system does not have virtualization framework support, change vmType to "qemu"`) + } + limaCfg.MountType = pointer.String("virtiofs") + } else if *lca.cfg.VMType == "qemu" { + limaCfg.MountType = pointer.String("reverse-sshfs") + } + limaCfg.Rosetta = limayaml.Rosetta{} + limaCfg.VMType = lca.cfg.VMType + toggleUserModeEmulationInstallationScript(limaCfg, true) + } + + return limaCfg, nil +} + +func toggleUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML, enabled bool) { + idx, hasScript := hasUserModeEmulationInstallationScript(limaCfg) + if !hasScript && enabled { + limaCfg.Provision = append(limaCfg.Provision, limayaml.Provision{ + Mode: "system", + Script: fmt.Sprintf(`%s +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER)}) + } else if hasScript && !enabled { + if len(limaCfg.Provision) > 0 { + limaCfg.Provision = append(limaCfg.Provision[:idx], limaCfg.Provision[idx+1:]...) + } + } +} + +func hasUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML) (int, bool) { + hasCrossArchToolInstallationScript := false + var scriptIdx int + for idx, prov := range limaCfg.Provision { + trimmed := strings.Trim(prov.Script, " ") + if !hasCrossArchToolInstallationScript && strings.HasPrefix(trimmed, USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER) { + hasCrossArchToolInstallationScript = true + scriptIdx = idx + } + } + + return scriptIdx, hasCrossArchToolInstallationScript +} + +func (lca *limaConfigApplier) supportsVirtualizationFramework() (bool, error) { + cmd := lca.cmdCreator.Create("sw_vers", "-productVersion") + out, err := cmd.Output() + if err != nil { + return false, fmt.Errorf("failed to run sw_vers command: %w", err) + } + + splitVer := strings.Split(string(out), ".") + if len(splitVer) <= 0 { + return false, fmt.Errorf("unexpected result from string split: %v", splitVer) + } + + majorVersionInt, err := strconv.ParseInt(splitVer[0], 10, 64) + if err != nil { + return false, fmt.Errorf("failed to parse split sw_vers output (%s) into int: %w", splitVer[0], err) + } + + if majorVersionInt >= 11 { + return true, nil + } + + return false, nil +} diff --git a/pkg/config/lima_config_applier_test.go b/pkg/config/lima_config_applier_test.go index ad8940f57..67f26c342 100644 --- a/pkg/config/lima_config_applier_test.go +++ b/pkg/config/lima_config_applier_test.go @@ -4,9 +4,7 @@ package config import ( - "errors" "fmt" - "io/fs" "testing" "github.com/golang/mock/gomock" @@ -26,20 +24,26 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { name string config *Finch path string - mockSvc func(fs afero.Fs, l *mocks.Logger) + isInit bool + mockSvc func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) postRunCheck func(t *testing.T, fs afero.Fs) want error }{ { name: "happy path", config: &Finch{ - Memory: pointer.String("2GiB"), - CPUs: pointer.Int(4), + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, - path: "/lima.yaml", - mockSvc: func(fs afero.Fs, l *mocks.Logger) { + path: "/lima.yaml", + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) + cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) + creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) }, postRunCheck: func(t *testing.T, fs afero.Fs) { buf, err := afero.ReadFile(fs, "/lima.yaml") @@ -50,27 +54,185 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { require.NoError(t, err) require.Equal(t, 4, *limaCfg.CPUs) require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "reverse-sshfs", *limaCfg.MountType) + require.Equal(t, "system", limaCfg.Provision[0].Mode) + require.Equal(t, `# cross-arch tools +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, limaCfg.Provision[0].Script) }, want: nil, }, { - name: "lima config file does not exist", - config: nil, - path: "/lima.yaml", - mockSvc: func(afs afero.Fs, l *mocks.Logger) {}, - postRunCheck: func(t *testing.T, mFs afero.Fs) { - _, err := afero.ReadFile(mFs, "/lima.yaml") - require.Equal(t, err, &fs.PathError{Op: "open", Path: "/lima.yaml", Err: errors.New("file does not exist")}) - }, - want: fmt.Errorf("failed to load the lima config file: %w", - &fs.PathError{Op: "open", Path: "/lima.yaml", Err: errors.New("file does not exist")}, - ), + name: "updates vmType and removes cross-arch provisioning script and network config", + config: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + VMType: pointer.String("vz"), + Rosetta: pointer.Bool(true), + }, + path: "/lima.yaml", + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB +cpus: 8 +vmType: "qemu" +provision: +- mode: system + script: | + # cross-arch tools + #!/bin/bash + dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`), 0o600) + require.NoError(t, err) + cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) + creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) + deps.EXPECT().OS().Return("darwin") + deps.EXPECT().Arch().Return("arm64") + }, + postRunCheck: func(t *testing.T, fs afero.Fs) { + buf, err := afero.ReadFile(fs, "/lima.yaml") + require.NoError(t, err) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "vz", *limaCfg.VMType) + require.Equal(t, "virtiofs", *limaCfg.MountType) + require.Equal(t, true, limaCfg.Rosetta.Enabled) + require.Equal(t, true, limaCfg.Rosetta.BinFmt) + require.Len(t, limaCfg.Provision, 0) + }, + want: nil, + }, + { + name: "updates vmType from vz to qemu and adds cross-arch provisioning script", + config: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, + path: "/lima.yaml", + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB +cpus: 8 +vmType: "vz" +rosetta: + enabled: true + binfmt: true +`), 0o600) + require.NoError(t, err) + cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) + creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) + }, + postRunCheck: func(t *testing.T, fs afero.Fs) { + buf, err := afero.ReadFile(fs, "/lima.yaml") + require.NoError(t, err) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "qemu", *limaCfg.VMType) + require.Equal(t, false, limaCfg.Rosetta.Enabled) + require.Equal(t, false, limaCfg.Rosetta.BinFmt) + require.Equal(t, "reverse-sshfs", *limaCfg.MountType) + require.Equal(t, "system", limaCfg.Provision[0].Mode) + require.Equal(t, `# cross-arch tools +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, limaCfg.Provision[0].Script) + }, + want: nil, + }, + { + name: "does not update lima config because isInit == false", + config: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + VMType: pointer.String("vz"), + Rosetta: pointer.Bool(false), + }, + path: "/lima.yaml", + isInit: false, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB +cpus: 8 +vmType: "qemu" +mountType: "reverse-sshfs" +provision: +- mode: system + script: | + # cross-arch tools + #!/bin/bash + dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`), 0o600) + require.NoError(t, err) + }, + postRunCheck: func(t *testing.T, fs afero.Fs) { + buf, err := afero.ReadFile(fs, "/lima.yaml") + require.NoError(t, err) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "qemu", *limaCfg.VMType) + require.Equal(t, "reverse-sshfs", *limaCfg.MountType) + require.Equal(t, "system", limaCfg.Provision[0].Mode) + require.Equal(t, `# cross-arch tools +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, limaCfg.Provision[0].Script) + }, + want: nil, + }, + { + name: "lima config file does not exist", + config: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), + }, + path: "/lima.yaml", + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) + require.NoError(t, err) + cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) + creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) + }, + postRunCheck: func(t *testing.T, fs afero.Fs) { + buf, err := afero.ReadFile(fs, "/lima.yaml") + require.NoError(t, err) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "reverse-sshfs", *limaCfg.MountType) + require.Equal(t, "system", limaCfg.Provision[0].Mode) + require.Equal(t, `# cross-arch tools +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, limaCfg.Provision[0].Script) + }, + want: nil, }, { name: "lima config file does not contain valid YAML", config: nil, path: "/lima.yaml", - mockSvc: func(fs afero.Fs, l *mocks.Logger) { + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { err := afero.WriteFile(fs, "/lima.yaml", []byte("this isn't YAML"), 0o600) require.NoError(t, err) }, @@ -91,33 +253,35 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { Memory: pointer.String("2GiB"), CPUs: pointer.Int(4), AdditionalDirectories: []AdditionalDirectory{{pointer.String("/Volumes")}}, + VMType: pointer.String("qemu"), + Rosetta: pointer.Bool(false), }, - path: "/lima.yaml", - mockSvc: func(fs afero.Fs, l *mocks.Logger) { + path: "/lima.yaml", + isInit: true, + mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) + cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) + creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) }, postRunCheck: func(t *testing.T, fs afero.Fs) { buf, err := afero.ReadFile(fs, "/lima.yaml") require.NoError(t, err) - // limayaml.LimaYAML has a required "images" field which will also get marshaled - wantYaml := `images: [] -cpus: 4 -memory: 2GiB -mounts: - - location: /Volumes - writable: true -` - require.Equal(t, wantYaml, string(buf)) var limaCfg limayaml.LimaYAML err = yaml.Unmarshal(buf, &limaCfg) require.NoError(t, err) require.Equal(t, 4, *limaCfg.CPUs) require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, "reverse-sshfs", *limaCfg.MountType) require.Equal(t, 1, len(limaCfg.Mounts)) require.Equal(t, "/Volumes", limaCfg.Mounts[0].Location) require.Equal(t, true, *limaCfg.Mounts[0].Writable) + require.Equal(t, "system", limaCfg.Provision[0].Mode) + require.Equal(t, `# cross-arch tools +#!/bin/bash +dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 +`, limaCfg.Provision[0].Script) }, want: nil, }, @@ -129,11 +293,14 @@ mounts: t.Parallel() ctrl := gomock.NewController(t) + cmd := mocks.NewCommand(ctrl) + cmdCreator := mocks.NewCommandCreator(ctrl) + deps := mocks.NewLimaConfigApplierSystemDeps(ctrl) l := mocks.NewLogger(ctrl) fs := afero.NewMemMapFs() - tc.mockSvc(fs, l) - got := NewLimaApplier(tc.config, fs, tc.path).Apply() + tc.mockSvc(fs, l, cmd, cmdCreator, deps) + got := NewLimaApplier(tc.config, cmdCreator, fs, tc.path, deps).Apply(tc.isInit) require.Equal(t, tc.want, got) tc.postRunCheck(t, fs) diff --git a/pkg/dependency/vmnet/vmnet_test.go b/pkg/dependency/vmnet/vmnet_test.go index c30e0844a..5f176ab04 100644 --- a/pkg/dependency/vmnet/vmnet_test.go +++ b/pkg/dependency/vmnet/vmnet_test.go @@ -27,5 +27,4 @@ func Test_newDeps(t *testing.T) { require.Equal(t, 3, len(got)) assert.IsType(t, (*binaries)(nil), got[0]) assert.IsType(t, (*sudoersFile)(nil), got[1]) - assert.IsType(t, (*overrideLimaConfig)(nil), got[2]) } diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index d7a2d6f31..26c269705 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -9,13 +9,13 @@ import ( "errors" "fmt" "io/fs" - "os" "path" limaStore "github.com/lima-vm/lima/pkg/store" "github.com/spf13/afero" "github.com/runfinch/finch/pkg/command" + "github.com/runfinch/finch/pkg/config" fpath "github.com/runfinch/finch/pkg/path" ) @@ -30,6 +30,14 @@ type UserDataDiskManager interface { EnsureUserDataDisk() error } +type QemuDiskInfo struct { + VirtualSize int `json:"virtual-size"` + Filename string `json:"filename"` + Format string `json:"format"` + ActualSize int `json:"actual-size"` + DirtyFlag bool `json:"dirty-flag"` +} + // fs functions required for setting up the user data disk. type diskFS interface { afero.Fs @@ -39,39 +47,68 @@ type diskFS interface { type userDataDiskManager struct { lcc command.LimaCmdCreator + ecc command.Creator fs diskFS finch fpath.Finch homeDir string + config *config.Finch } // NewUserDataDiskManager is a constructor for UserDataDiskManager. func NewUserDataDiskManager( lcc command.LimaCmdCreator, + ecc command.Creator, fs diskFS, finch fpath.Finch, homeDir string, + config *config.Finch, ) UserDataDiskManager { return &userDataDiskManager{ lcc: lcc, + ecc: ecc, fs: fs, finch: finch, homeDir: homeDir, + config: config, } } // EnsureUserDataDisk checks the current disk configuration and fixes it if needed. func (m *userDataDiskManager) EnsureUserDataDisk() error { if m.limaDiskExists() { + diskPath := m.finch.UserDataDiskPath(m.homeDir) + + if *m.config.VMType == "vz" { + info, err := m.getDiskInfo(diskPath) + if err != nil { + return err + } + + // Convert the persistent disk file to RAW before Lima starts. + // Lima also does this, but since Finch uses a symlink to this file, lima won't create the new RAW file + // in the persistent location, but in its own _disks directory. + if info.Format != "raw" { + if err := m.convertToRaw(diskPath); err != nil { + return err + } + + // since convertToRaw moves the disk, the symlink needs to be recreated + if err := m.attachPersistentDiskToLimaDisk(); err != nil { + return err + } + } + } + + // if the file is not a symlink, loc will be an empty string + // both os.Readlink() and UserDataDiskPath return absolute paths, so they will be equal if equivalent limaPath := fmt.Sprintf("%s/_disks/%s/datadisk", m.finch.LimaHomePath(), diskName) loc, err := m.fs.ReadlinkIfPossible(limaPath) if err != nil { return err } - // if the file is not a symlink, loc will be an empty string - // both os.Readlink() and UserDataDiskPath return absolute paths, so they will be equal if equivalent - if loc != m.finch.UserDataDiskPath(m.homeDir) { - err := m.attachPersistentDiskToLimaDisk() - if err != nil { + + if loc != diskPath { + if err := m.attachPersistentDiskToLimaDisk(); err != nil { return err } } @@ -79,8 +116,7 @@ func (m *userDataDiskManager) EnsureUserDataDisk() error { if err := m.createLimaDisk(); err != nil { return err } - err := m.attachPersistentDiskToLimaDisk() - if err != nil { + if err := m.attachPersistentDiskToLimaDisk(); err != nil { return err } } @@ -114,8 +150,49 @@ func (m *userDataDiskManager) limaDiskExists() bool { return diskListOutput.Name == diskName } +func (m *userDataDiskManager) getDiskInfo(diskPath string) (*QemuDiskInfo, error) { + out, err := m.ecc.Create( + path.Join(m.finch.QEMUBinDir(), "qemu-img"), + "info", + "--output=json", + diskPath, + ).CombinedOutput() + + if err != nil { + return nil, fmt.Errorf("failed to get disk info for disk at %q: %w", diskPath, err) + } + + var diskInfoJson QemuDiskInfo + if err = json.Unmarshal(out, &diskInfoJson); err != nil { + return nil, fmt.Errorf("failed to unmarshal disk info JSON for disk at %q: %w", diskPath, err) + } + + return &diskInfoJson, nil +} + +func (m *userDataDiskManager) convertToRaw(diskPath string) error { + qcowPath := fmt.Sprintf("%s.qcow2", diskPath) + if err := m.fs.Rename(diskPath, qcowPath); err != nil { + return fmt.Errorf("faied to rename disk: %w", err) + } + if _, err := m.ecc.Create( + path.Join(m.finch.QEMUBinDir(), "qemu-img"), + "convert", + "-f", + "qcow2", + "-O", + "raw", + qcowPath, + diskPath, + ).CombinedOutput(); err != nil { + return fmt.Errorf("failed to convert disk %q from qcow2 to raw: %w", diskPath, err) + } + + return nil +} + func (m *userDataDiskManager) createLimaDisk() error { - cmd := m.lcc.CreateWithoutStdio("disk", "create", diskName, "--size", diskSize) + cmd := m.lcc.CreateWithoutStdio("disk", "create", diskName, "--size", diskSize, "--format", "raw") if logs, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to create disk, debug logs:\n%s", logs) } @@ -127,15 +204,13 @@ func (m *userDataDiskManager) attachPersistentDiskToLimaDisk() error { if !m.persistentDiskExists() { disksDir := path.Dir(m.finch.UserDataDiskPath(m.homeDir)) _, err := m.fs.Stat(disksDir) - if os.IsNotExist(err) { - err := m.fs.MkdirAll(disksDir, 0o755) - if err != nil { + if errors.Is(err, fs.ErrNotExist) { + if err := m.fs.MkdirAll(disksDir, 0o755); err != nil { return fmt.Errorf("could not create persistent disk directory: %w", err) } } - err = m.fs.Rename(limaPath, m.finch.UserDataDiskPath(m.homeDir)) - if err != nil { - return err + if err = m.fs.Rename(limaPath, m.finch.UserDataDiskPath(m.homeDir)); err != nil { + return fmt.Errorf("could not move data disk to persistent path: %w", err) } } diff --git a/pkg/disk/disk_test.go b/pkg/disk/disk_test.go index fbf4cdf68..43dfefcf6 100644 --- a/pkg/disk/disk_test.go +++ b/pkg/disk/disk_test.go @@ -4,13 +4,16 @@ package disk import ( + "fmt" "io/fs" "path" "testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "github.com/xorcare/pointer" + "github.com/runfinch/finch/pkg/config" "github.com/runfinch/finch/pkg/mocks" fpath "github.com/runfinch/finch/pkg/path" ) @@ -20,11 +23,12 @@ func TestDisk_NewUserDataDiskManager(t *testing.T) { ctrl := gomock.NewController(t) lcc := mocks.NewLimaCmdCreator(ctrl) + ecc := mocks.NewCommandCreator(ctrl) dfs := mocks.NewMockdiskFS(ctrl) finch := fpath.Finch("mock_finch") homeDir := "mock_home" - NewUserDataDiskManager(lcc, dfs, finch, homeDir) + NewUserDataDiskManager(lcc, ecc, dfs, finch, homeDir, &config.Finch{}) } func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { @@ -36,21 +40,51 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { limaPath := path.Join(finch.LimaHomePath(), "_disks", diskName, "datadisk") lockPath := path.Join(finch.LimaHomePath(), "_disks", diskName, "in_use_by") mockListArgs := []string{"disk", "ls", diskName, "--json"} - mockCreateArgs := []string{"disk", "create", diskName, "--size", diskSize} + mockCreateArgs := []string{"disk", "create", diskName, "--size", diskSize, "--format", "raw"} mockUnlockArgs := []string{"disk", "unlock", diskName} + mockQemuImgExePath := "mock_finch/lima/bin/qemu-img" + mockDiskInfoArgs := []string{ + "info", + "--output=json", + finch.UserDataDiskPath(homeDir), + } + mockQemuBackupDiskPath := fmt.Sprintf("%s.qcow2", finch.UserDataDiskPath(homeDir)) + mockDiskConvertArgs := []string{ + "convert", + "-f", + "qcow2", + "-O", + "raw", + mockQemuBackupDiskPath, + finch.UserDataDiskPath(homeDir), + } //nolint:lll // line cannot be shortened without losing functionality listSuccessOutput := []byte("{\"name\":\"finch\",\"size\":5,\"dir\":\"mock_dir\",\"instance\":\"\",\"instanceDir\":\"\",\"mountPoint\":\"/mnt/lima-finch\"}") + diskInfoQCOW2SuccessOutput := []byte(` +{ + "virtual-size": 53687091200, + "filename": "mock_home/.finch/.disks/datadisk", + "format": "qcow2", + "actual-size": 6107136, + "dirty-flag": false +} +`) + testCases := []struct { name string + cfg *config.Finch wantErr error - mockSvc func(*mocks.LimaCmdCreator, *mocks.MockdiskFS, *mocks.Command) + mockSvc func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) }{ { - name: "create and save disk", + name: "create and save disk", + cfg: &config.Finch{ + VMType: pointer.String("qemu"), + }, wantErr: nil, - mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command) { + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) cmd.EXPECT().Output().Return([]byte(""), nil) @@ -68,9 +102,12 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { }, }, { - name: "disk already exists", + name: "disk already exists", + cfg: &config.Finch{ + VMType: pointer.String("qemu"), + }, wantErr: nil, - mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command) { + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) cmd.EXPECT().Output().Return(listSuccessOutput, nil) @@ -80,9 +117,12 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { }, }, { - name: "disk exists but has not been saved", + name: "disk exists but has not been saved", + cfg: &config.Finch{ + VMType: pointer.String("qemu"), + }, wantErr: nil, - mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command) { + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) cmd.EXPECT().Output().Return(listSuccessOutput, nil) @@ -100,9 +140,12 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { }, }, { - name: "disk does not exist but a persistent disk does", + name: "disk does not exist but a persistent disk does", + cfg: &config.Finch{ + VMType: pointer.String("qemu"), + }, wantErr: nil, - mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command) { + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) cmd.EXPECT().Output().Return([]byte(""), nil) @@ -120,9 +163,12 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { }, }, { - name: "disk already exists but is locked", + name: "disk already exists but is locked", + cfg: &config.Finch{ + VMType: pointer.String("qemu"), + }, wantErr: nil, - mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command) { + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) cmd.EXPECT().Output().Return(listSuccessOutput, nil) @@ -133,6 +179,36 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { cmd.EXPECT().CombinedOutput().Return(nil, nil) }, }, + { + name: "disk exists and using vz mode, but disk is the wrong format", + cfg: &config.Finch{ + VMType: pointer.String("vz"), + }, + wantErr: nil, + mockSvc: func(lcc *mocks.LimaCmdCreator, dfs *mocks.MockdiskFS, cmd *mocks.Command, ecc *mocks.CommandCreator) { + lcc.EXPECT().CreateWithoutStdio(mockListArgs).Return(cmd) + cmd.EXPECT().Output().Return(listSuccessOutput, nil) + + ecc.EXPECT().Create(mockQemuImgExePath, mockDiskInfoArgs).Return(cmd) + cmd.EXPECT().CombinedOutput().Return(diskInfoQCOW2SuccessOutput, nil) + + dfs.EXPECT().Rename(finch.UserDataDiskPath(homeDir), mockQemuBackupDiskPath).Return(nil) + + ecc.EXPECT().Create(mockQemuImgExePath, mockDiskConvertArgs).Return(cmd) + cmd.EXPECT().CombinedOutput().Return([]byte(""), nil) + + dfs.EXPECT().Stat(finch.UserDataDiskPath(homeDir)).Return(nil, fs.ErrNotExist) + dfs.EXPECT().Stat(path.Dir(finch.UserDataDiskPath(homeDir))).Return(nil, nil) + dfs.EXPECT().Rename(limaPath, finch.UserDataDiskPath(homeDir)).Return(nil) + + dfs.EXPECT().ReadlinkIfPossible(limaPath).Return(finch.UserDataDiskPath(homeDir), nil) + + dfs.EXPECT().Stat(limaPath).Return(nil, fs.ErrNotExist) + dfs.EXPECT().SymlinkIfPossible(finch.UserDataDiskPath(homeDir), limaPath).Return(nil) + + dfs.EXPECT().Stat(lockPath).Return(nil, fs.ErrNotExist) + }, + }, } for _, tc := range testCases { @@ -142,10 +218,11 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { ctrl := gomock.NewController(t) lcc := mocks.NewLimaCmdCreator(ctrl) + ecc := mocks.NewCommandCreator(ctrl) dfs := mocks.NewMockdiskFS(ctrl) cmd := mocks.NewCommand(ctrl) - tc.mockSvc(lcc, dfs, cmd) - dm := NewUserDataDiskManager(lcc, dfs, finch, homeDir) + tc.mockSvc(lcc, dfs, cmd, ecc) + dm := NewUserDataDiskManager(lcc, ecc, dfs, finch, homeDir, tc.cfg) err := dm.EnsureUserDataDisk() assert.Equal(t, tc.wantErr, err) }) diff --git a/pkg/mocks/pkg_config_lima_config_applier.go b/pkg/mocks/pkg_config_lima_config_applier.go index 045bf0f98..968e25947 100644 --- a/pkg/mocks/pkg_config_lima_config_applier.go +++ b/pkg/mocks/pkg_config_lima_config_applier.go @@ -37,15 +37,15 @@ func (m *LimaConfigApplier) EXPECT() *LimaConfigApplierMockRecorder { } // Apply mocks base method. -func (m *LimaConfigApplier) Apply() error { +func (m *LimaConfigApplier) Apply(arg0 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Apply") + ret := m.ctrl.Call(m, "Apply", arg0) ret0, _ := ret[0].(error) return ret0 } // Apply indicates an expected call of Apply. -func (mr *LimaConfigApplierMockRecorder) Apply() *gomock.Call { +func (mr *LimaConfigApplierMockRecorder) Apply(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*LimaConfigApplier)(nil).Apply)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*LimaConfigApplier)(nil).Apply), arg0) } diff --git a/pkg/mocks/pkg_config_lima_config_applier_system_deps.go b/pkg/mocks/pkg_config_lima_config_applier_system_deps.go new file mode 100644 index 000000000..676523039 --- /dev/null +++ b/pkg/mocks/pkg_config_lima_config_applier_system_deps.go @@ -0,0 +1,65 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/runfinch/finch/pkg/config (interfaces: LimaConfigApplierSystemDeps) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// LimaConfigApplierSystemDeps is a mock of LimaConfigApplierSystemDeps interface. +type LimaConfigApplierSystemDeps struct { + ctrl *gomock.Controller + recorder *LimaConfigApplierSystemDepsMockRecorder +} + +// LimaConfigApplierSystemDepsMockRecorder is the mock recorder for LimaConfigApplierSystemDeps. +type LimaConfigApplierSystemDepsMockRecorder struct { + mock *LimaConfigApplierSystemDeps +} + +// NewLimaConfigApplierSystemDeps creates a new mock instance. +func NewLimaConfigApplierSystemDeps(ctrl *gomock.Controller) *LimaConfigApplierSystemDeps { + mock := &LimaConfigApplierSystemDeps{ctrl: ctrl} + mock.recorder = &LimaConfigApplierSystemDepsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *LimaConfigApplierSystemDeps) EXPECT() *LimaConfigApplierSystemDepsMockRecorder { + return m.recorder +} + +// Arch mocks base method. +func (m *LimaConfigApplierSystemDeps) Arch() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Arch") + ret0, _ := ret[0].(string) + return ret0 +} + +// Arch indicates an expected call of Arch. +func (mr *LimaConfigApplierSystemDepsMockRecorder) Arch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Arch", reflect.TypeOf((*LimaConfigApplierSystemDeps)(nil).Arch)) +} + +// OS mocks base method. +func (m *LimaConfigApplierSystemDeps) OS() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OS") + ret0, _ := ret[0].(string) + return ret0 +} + +// OS indicates an expected call of OS. +func (mr *LimaConfigApplierSystemDepsMockRecorder) OS() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OS", reflect.TypeOf((*LimaConfigApplierSystemDeps)(nil).OS)) +} diff --git a/pkg/system/stdlib.go b/pkg/system/stdlib.go index 0f9f1d1c7..dc2102971 100644 --- a/pkg/system/stdlib.go +++ b/pkg/system/stdlib.go @@ -61,3 +61,11 @@ func (s *StdLib) NumCPU() int { func (s *StdLib) ReadMemStats(st *runtime.MemStats) { runtime.ReadMemStats(st) } + +func (s *StdLib) Arch() string { + return runtime.GOARCH +} + +func (s *StdLib) OS() string { + return runtime.GOOS +} diff --git a/pkg/system/system.go b/pkg/system/system.go index 92c6323e9..9e73dd782 100644 --- a/pkg/system/system.go +++ b/pkg/system/system.go @@ -71,3 +71,13 @@ type StderrGetter interface { type RuntimeCPUGetter interface { NumCPU() int } + +// RuntimeCPUGetter mocks out runtime.GOARCH. +type RuntimeArchGetter interface { + Arch() string +} + +// RuntimeCPUGetter mocks out runtime.GOOS. +type RuntimeOSGetter interface { + OS() string +} From 8fb9eb2f352bde2921700f13b372cb8651dd5e4d Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 10 Mar 2023 18:23:56 -0500 Subject: [PATCH 02/19] Fix comments in pkg/system Signed-off-by: Justin Alvarez --- pkg/system/system.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/system/system.go b/pkg/system/system.go index 9e73dd782..9c589b4da 100644 --- a/pkg/system/system.go +++ b/pkg/system/system.go @@ -72,12 +72,12 @@ type RuntimeCPUGetter interface { NumCPU() int } -// RuntimeCPUGetter mocks out runtime.GOARCH. +// RuntimeArchGetter mocks out runtime.GOARCH. type RuntimeArchGetter interface { Arch() string } -// RuntimeCPUGetter mocks out runtime.GOOS. +// RuntimeOSGetter mocks out runtime.GOOS. type RuntimeOSGetter interface { OS() string } From 9ec9600d12a97b0e1f1c12d45c0e82290015d462 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 10 Mar 2023 18:30:19 -0500 Subject: [PATCH 03/19] Fix linting errors in pkg/disk Signed-off-by: Justin Alvarez --- pkg/disk/disk.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index 26c269705..e216e443f 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -30,7 +30,7 @@ type UserDataDiskManager interface { EnsureUserDataDisk() error } -type QemuDiskInfo struct { +type qemuDiskInfo struct { VirtualSize int `json:"virtual-size"` Filename string `json:"filename"` Format string `json:"format"` @@ -150,7 +150,7 @@ func (m *userDataDiskManager) limaDiskExists() bool { return diskListOutput.Name == diskName } -func (m *userDataDiskManager) getDiskInfo(diskPath string) (*QemuDiskInfo, error) { +func (m *userDataDiskManager) getDiskInfo(diskPath string) (*qemuDiskInfo, error) { out, err := m.ecc.Create( path.Join(m.finch.QEMUBinDir(), "qemu-img"), "info", @@ -162,12 +162,12 @@ func (m *userDataDiskManager) getDiskInfo(diskPath string) (*QemuDiskInfo, error return nil, fmt.Errorf("failed to get disk info for disk at %q: %w", diskPath, err) } - var diskInfoJson QemuDiskInfo - if err = json.Unmarshal(out, &diskInfoJson); err != nil { + var diskInfoJSON qemuDiskInfo + if err = json.Unmarshal(out, &diskInfoJSON); err != nil { return nil, fmt.Errorf("failed to unmarshal disk info JSON for disk at %q: %w", diskPath, err) } - return &diskInfoJson, nil + return &diskInfoJSON, nil } func (m *userDataDiskManager) convertToRaw(diskPath string) error { From 6946f610418ad90456e49207dcbbb0780541e0df Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 10 Mar 2023 18:30:29 -0500 Subject: [PATCH 04/19] Fix linting errors in pkg/config Signed-off-by: Justin Alvarez --- pkg/config/lima_config_applier.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/config/lima_config_applier.go b/pkg/config/lima_config_applier.go index 002ae5fe3..543cfcdc1 100644 --- a/pkg/config/lima_config_applier.go +++ b/pkg/config/lima_config_applier.go @@ -16,9 +16,9 @@ import ( "gopkg.in/yaml.v3" ) -const USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER = "# cross-arch tools" +const userModeEmulationProvisioningScriptHeader = "# cross-arch tools" -// LoadSystemDeps contains the system dependencies for Load. +// LimaConfigApplierSystemDeps contains the system dependencies for LimaConfigApplier. // //go:generate mockgen -copyright_file=../../copyright_header -destination=../mocks/pkg_config_lima_config_applier_system_deps.go -package=mocks -mock_names LimaConfigApplierSystemDeps=LimaConfigApplierSystemDeps . LimaConfigApplierSystemDeps type LimaConfigApplierSystemDeps interface { @@ -145,7 +145,7 @@ func toggleUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML, enabl Script: fmt.Sprintf(`%s #!/bin/bash dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 -`, USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER)}) +`, userModeEmulationProvisioningScriptHeader)}) } else if hasScript && !enabled { if len(limaCfg.Provision) > 0 { limaCfg.Provision = append(limaCfg.Provision[:idx], limaCfg.Provision[idx+1:]...) @@ -158,7 +158,7 @@ func hasUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML) (int, bo var scriptIdx int for idx, prov := range limaCfg.Provision { trimmed := strings.Trim(prov.Script, " ") - if !hasCrossArchToolInstallationScript && strings.HasPrefix(trimmed, USER_MODE_EMULATION_INSTALLATION_SCRIPT_HEADER) { + if !hasCrossArchToolInstallationScript && strings.HasPrefix(trimmed, userModeEmulationProvisioningScriptHeader) { hasCrossArchToolInstallationScript = true scriptIdx = idx } From a3aa3af30db4b507545fd54f1d0abd9236cbdb62 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 10 Mar 2023 18:51:01 -0500 Subject: [PATCH 05/19] pkg/disk: fix formatting Signed-off-by: Justin Alvarez --- pkg/disk/disk.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index e216e443f..553c260a0 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -157,7 +157,6 @@ func (m *userDataDiskManager) getDiskInfo(diskPath string) (*qemuDiskInfo, error "--output=json", diskPath, ).CombinedOutput() - if err != nil { return nil, fmt.Errorf("failed to get disk info for disk at %q: %w", diskPath, err) } From 1a2001a6890e6ddf62ff66aa468fece44d7cd947 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 10 Mar 2023 18:51:17 -0500 Subject: [PATCH 06/19] pkg/config: fix more linting warnings Signed-off-by: Justin Alvarez --- pkg/config/lima_config_applier.go | 23 +++++--- pkg/config/lima_config_applier_test.go | 72 +++++++++++++++++++++----- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/pkg/config/lima_config_applier.go b/pkg/config/lima_config_applier.go index 543cfcdc1..7aca049b8 100644 --- a/pkg/config/lima_config_applier.go +++ b/pkg/config/lima_config_applier.go @@ -9,11 +9,12 @@ import ( "strings" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/runfinch/finch/pkg/command" - "github.com/runfinch/finch/pkg/system" "github.com/spf13/afero" "github.com/xorcare/pointer" "gopkg.in/yaml.v3" + + "github.com/runfinch/finch/pkg/command" + "github.com/runfinch/finch/pkg/system" ) const userModeEmulationProvisioningScriptHeader = "# cross-arch tools" @@ -38,7 +39,13 @@ var _ LimaConfigApplier = (*limaConfigApplier)(nil) // NewLimaApplier creates a new LimaConfigApplier that // applies lima configuration changes by writing to the lima config file on the disk. -func NewLimaApplier(cfg *Finch, cmdCreator command.Creator, fs afero.Fs, limaConfigPath string, systemDeps LimaConfigApplierSystemDeps) LimaConfigApplier { +func NewLimaApplier( + cfg *Finch, + cmdCreator command.Creator, + fs afero.Fs, + limaConfigPath string, + systemDeps LimaConfigApplierSystemDeps, +) LimaConfigApplier { return &limaConfigApplier{ cfg: cfg, cmdCreator: cmdCreator, @@ -101,14 +108,13 @@ func (lca *limaConfigApplier) Apply(isInit bool) error { // applyInit changes settings that will only apply to the VM after a new init. func (lca *limaConfigApplier) applyInit(limaCfg *limayaml.LimaYAML) (*limayaml.LimaYAML, error) { hasSupport, hasSupportErr := lca.supportsVirtualizationFramework() - if *lca.cfg.Rosetta == true && + if *lca.cfg.Rosetta && lca.systemDeps.OS() == "darwin" && lca.systemDeps.Arch() == "arm64" { - if hasSupportErr != nil { return nil, fmt.Errorf("failed to check for virtualization framework support: %w", hasSupportErr) } - if hasSupport == false { + if !hasSupport { return nil, fmt.Errorf(`system does not have virtualization framework support, change vmType to "qemu"`) } @@ -145,7 +151,8 @@ func toggleUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML, enabl Script: fmt.Sprintf(`%s #!/bin/bash dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-user-static-arm qemu-user-static-x86 -`, userModeEmulationProvisioningScriptHeader)}) +`, userModeEmulationProvisioningScriptHeader), + }) } else if hasScript && !enabled { if len(limaCfg.Provision) > 0 { limaCfg.Provision = append(limaCfg.Provision[:idx], limaCfg.Provision[idx+1:]...) @@ -175,7 +182,7 @@ func (lca *limaConfigApplier) supportsVirtualizationFramework() (bool, error) { } splitVer := strings.Split(string(out), ".") - if len(splitVer) <= 0 { + if len(splitVer) == 0 { return false, fmt.Errorf("unexpected result from string split: %v", splitVer) } diff --git a/pkg/config/lima_config_applier_test.go b/pkg/config/lima_config_applier_test.go index 67f26c342..420496ddf 100644 --- a/pkg/config/lima_config_applier_test.go +++ b/pkg/config/lima_config_applier_test.go @@ -21,11 +21,17 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { t.Parallel() testCases := []struct { - name string - config *Finch - path string - isInit bool - mockSvc func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) + name string + config *Finch + path string + isInit bool + mockSvc func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) postRunCheck func(t *testing.T, fs afero.Fs) want error }{ @@ -39,7 +45,13 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { }, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) @@ -73,7 +85,13 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us }, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB cpus: 8 vmType: "qemu" @@ -117,7 +135,13 @@ provision: }, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB cpus: 8 vmType: "vz" @@ -160,7 +184,13 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us }, path: "/lima.yaml", isInit: false, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte(`memory: 4GiB cpus: 8 vmType: "qemu" @@ -203,7 +233,13 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us }, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) @@ -232,7 +268,13 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us config: nil, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("this isn't YAML"), 0o600) require.NoError(t, err) }, @@ -258,7 +300,13 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us }, path: "/lima.yaml", isInit: true, - mockSvc: func(fs afero.Fs, l *mocks.Logger, cmd *mocks.Command, creator *mocks.CommandCreator, deps *mocks.LimaConfigApplierSystemDeps) { + mockSvc: func( + fs afero.Fs, + l *mocks.Logger, + cmd *mocks.Command, + creator *mocks.CommandCreator, + deps *mocks.LimaConfigApplierSystemDeps, + ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) From becc675d134c692969f04c23ef0b12461354131f Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 17 Mar 2023 19:53:33 -0400 Subject: [PATCH 07/19] update README, add basic tests when using Virtualzation.framework / Rosetta Signed-off-by: Justin Alvarez --- README.md | 11 +++ .../virtualization_framework_rosetta_test.go | 85 +++++++++++++++++++ e2e/vm/vm_test.go | 2 + pkg/config/config.go | 27 ++++++ pkg/config/lima_config_applier.go | 27 +----- 5 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 e2e/vm/virtualization_framework_rosetta_test.go diff --git a/README.md b/README.md index 7c42586fe..a28050e7d 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,17 @@ memory: 4GiB additional_directories: # the path of each additional directory. - path: /Volumes +# vmType (Experimental): sets which Hypervisor to use to launch the VM. (optional) +# Only takes effect when a new VM is launched (only on vm init). +# One of: "qemu", "vz". +# - "qemu" (default): Uses QEMU as the Hypervisor. +# - "vz": Uses Virtualization.framework as the Hypervisor. +vmType: "qemu" +# rosetta (Experimental): sets whether to enable Rosetta as the binfmt_misc handler inside the VM. (optional) +# Only takes effect when a new VM is launched (only on vm init). +# Only available when using vmType "vz" on Apple Silicon running macOS 13+. +# If true, also sets vmType to "vz". +rosetta: false ``` ### FAQ diff --git a/e2e/vm/virtualization_framework_rosetta_test.go b/e2e/vm/virtualization_framework_rosetta_test.go new file mode 100644 index 000000000..32c51e9a5 --- /dev/null +++ b/e2e/vm/virtualization_framework_rosetta_test.go @@ -0,0 +1,85 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package vm + +import ( + "os/exec" + "path/filepath" + "runtime" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + "github.com/runfinch/common-tests/command" + "github.com/runfinch/common-tests/option" + "github.com/runfinch/common-tests/tests" + + "github.com/runfinch/finch/e2e" + finch_cmd "github.com/runfinch/finch/pkg/command" + "github.com/runfinch/finch/pkg/config" +) + +var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed bool) { + ginkgo.Describe("Virtualization framework", ginkgo.Ordered, func() { + var limaConfigFilePath string + + supportsVz, supportsVzErr := config.SupportsVirtualizationFramework(finch_cmd.NewExecCmdCreator()) + gomega.Expect(supportsVzErr).ShouldNot(gomega.HaveOccurred()) + + ginkgo.BeforeEach(func() { + origFinchCfg := readFile(finchConfigFilePath) + limaConfigFilePath = defaultLimaConfigFilePath + if installed { + path, err := exec.LookPath(e2e.InstalledTestSubject) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + realFinchPath, err := filepath.EvalSymlinks(path) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + } + origLimaCfg := readFile(limaConfigFilePath) + + command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + + ginkgo.DeferCleanup(func() { + writeFile(finchConfigFilePath, origFinchCfg) + writeFile(limaConfigFilePath, origLimaCfg) + }) + }) + + ginkgo.FDescribe("Virtualization framework", ginkgo.Ordered, func() { + ginkgo.BeforeEach(func() { + if !supportsVz { + ginkgo.Skip("Skipping because system does not support Virtualization.framework") + } + + writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: false")) + initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() + gomega.Expect(initCmdSession).Should(gexec.Exit(0)) + }) + + // Run sanity check tests + tests.Build(o) + tests.Run(&tests.RunOption{BaseOpt: o, CGMode: tests.Unified, DefaultHostGatewayIP: "192.168.5.2"}) + tests.Port(o) + }) + + ginkgo.Describe("Virtualization framework and Rosetta", ginkgo.Ordered, func() { + ginkgo.BeforeEach(func() { + if !supportsVz || runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { + ginkgo.Skip("Skipping because system does not support Rosetta") + } + + writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) + initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() + gomega.Expect(initCmdSession).Should(gexec.Exit(0)) + }) + + // Run sanity check tests + tests.Build(o) + tests.Run(&tests.RunOption{BaseOpt: o, CGMode: tests.Unified, DefaultHostGatewayIP: "192.168.5.2"}) + tests.Port(o) + }) + }) +} diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index b938df6e3..5b9cd4def 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -9,6 +9,7 @@ import ( "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" + "github.com/runfinch/common-tests/command" "github.com/runfinch/finch/e2e" @@ -42,6 +43,7 @@ func TestVM(t *testing.T) { testAdditionalDisk(o) testConfig(o, *e2e.Installed) testVersion(o) + testVirtualizationFrameworkAndRosetta(o, *e2e.Installed) }) gomega.RegisterFailHandler(ginkgo.Fail) diff --git a/pkg/config/config.go b/pkg/config/config.go index f1a436199..174c35d9d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -13,11 +13,14 @@ import ( "errors" "fmt" "path" + "strconv" + "strings" "github.com/lima-vm/lima/pkg/limayaml" "github.com/spf13/afero" "gopkg.in/yaml.v3" + "github.com/runfinch/finch/pkg/command" "github.com/runfinch/finch/pkg/flog" "github.com/runfinch/finch/pkg/fmemory" "github.com/runfinch/finch/pkg/system" @@ -149,3 +152,27 @@ func Load(fs afero.Fs, cfgPath string, log flog.Logger, systemDeps LoadSystemDep return defCfg, nil } + +func SupportsVirtualizationFramework(cmdCreator command.Creator) (bool, error) { + cmd := cmdCreator.Create("sw_vers", "-productVersion") + out, err := cmd.Output() + if err != nil { + return false, fmt.Errorf("failed to run sw_vers command: %w", err) + } + + splitVer := strings.Split(string(out), ".") + if len(splitVer) == 0 { + return false, fmt.Errorf("unexpected result from string split: %v", splitVer) + } + + majorVersionInt, err := strconv.ParseInt(splitVer[0], 10, 64) + if err != nil { + return false, fmt.Errorf("failed to parse split sw_vers output (%s) into int: %w", splitVer[0], err) + } + + if majorVersionInt >= 11 { + return true, nil + } + + return false, nil +} diff --git a/pkg/config/lima_config_applier.go b/pkg/config/lima_config_applier.go index 7aca049b8..4cfdc21cf 100644 --- a/pkg/config/lima_config_applier.go +++ b/pkg/config/lima_config_applier.go @@ -5,7 +5,6 @@ package config import ( "fmt" - "strconv" "strings" "github.com/lima-vm/lima/pkg/limayaml" @@ -107,7 +106,7 @@ func (lca *limaConfigApplier) Apply(isInit bool) error { // applyInit changes settings that will only apply to the VM after a new init. func (lca *limaConfigApplier) applyInit(limaCfg *limayaml.LimaYAML) (*limayaml.LimaYAML, error) { - hasSupport, hasSupportErr := lca.supportsVirtualizationFramework() + hasSupport, hasSupportErr := SupportsVirtualizationFramework(lca.cmdCreator) if *lca.cfg.Rosetta && lca.systemDeps.OS() == "darwin" && lca.systemDeps.Arch() == "arm64" { @@ -173,27 +172,3 @@ func hasUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML) (int, bo return scriptIdx, hasCrossArchToolInstallationScript } - -func (lca *limaConfigApplier) supportsVirtualizationFramework() (bool, error) { - cmd := lca.cmdCreator.Create("sw_vers", "-productVersion") - out, err := cmd.Output() - if err != nil { - return false, fmt.Errorf("failed to run sw_vers command: %w", err) - } - - splitVer := strings.Split(string(out), ".") - if len(splitVer) == 0 { - return false, fmt.Errorf("unexpected result from string split: %v", splitVer) - } - - majorVersionInt, err := strconv.ParseInt(splitVer[0], 10, 64) - if err != nil { - return false, fmt.Errorf("failed to parse split sw_vers output (%s) into int: %w", splitVer[0], err) - } - - if majorVersionInt >= 11 { - return true, nil - } - - return false, nil -} From 8986927661ffe5853cc219f94b6bda9cdaa069e1 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Fri, 24 Mar 2023 14:43:48 -0400 Subject: [PATCH 08/19] update submodule and makefile Signed-off-by: Justin Alvarez --- .gitmodules | 2 +- Makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 6dd336705..dae937af0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "deps/finch-core"] path = deps/finch-core url = https://github.com/pendo324/finch-core.git - branch = lima-with-vz-extra-disk + branch = lima-with-vz-extra-disk-local diff --git a/Makefile b/Makefile index 1fe2730c7..77020b209 100644 --- a/Makefile +++ b/Makefile @@ -34,13 +34,13 @@ ifneq (,$(findstring arm64,$(ARCH))) SUPPORTED_ARCH = true LIMA_ARCH = aarch64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/aarch64/images/ - FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.aarch64-20230317205128.qcow2 + FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.aarch64-20230321224649.qcow2 LIMA_URL ?= https://deps.runfinch.com/aarch64/lima-and-qemu.macos-aarch64.1678826933.tar.gz else ifneq (,$(findstring x86_64,$(ARCH))) SUPPORTED_ARCH = true LIMA_ARCH = x86_64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/x86_64/images/ - FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.x86_64-20230317204632.qcow2 + FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.x86_64-20230321224635.qcow2 LIMA_URL ?= https://deps.runfinch.com/x86-64/lima-and-qemu.macos-x86_64.1678817277.tar.gz endif From d1f8adab672ada22804b0e089090d1940068caeb Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 13:25:30 -0400 Subject: [PATCH 09/19] reset makefile / submodules Signed-off-by: Justin Alvarez --- .gitmodules | 3 +-- Makefile | 9 ++++----- deps/finch-core | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.gitmodules b/.gitmodules index dae937af0..5d08fcc22 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "deps/finch-core"] path = deps/finch-core - url = https://github.com/pendo324/finch-core.git - branch = lima-with-vz-extra-disk-local + url = https://github.com/runfinch/finch-core.git diff --git a/Makefile b/Makefile index 77020b209..4a858209d 100644 --- a/Makefile +++ b/Makefile @@ -35,13 +35,13 @@ ifneq (,$(findstring arm64,$(ARCH))) LIMA_ARCH = aarch64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/aarch64/images/ FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.aarch64-20230321224649.qcow2 - LIMA_URL ?= https://deps.runfinch.com/aarch64/lima-and-qemu.macos-aarch64.1678826933.tar.gz + LIMA_URL ?= https://deps.runfinch.com/aarch64/lima-and-qemu.macos-aarch64.1679936560.tar.gz else ifneq (,$(findstring x86_64,$(ARCH))) SUPPORTED_ARCH = true LIMA_ARCH = x86_64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/x86_64/images/ FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.x86_64-20230321224635.qcow2 - LIMA_URL ?= https://deps.runfinch.com/x86-64/lima-and-qemu.macos-x86_64.1678817277.tar.gz + LIMA_URL ?= https://deps.runfinch.com/x86-64/lima-and-qemu.macos-x86_64.1679936560.tar.gz endif FINCH_OS_HASH := `shasum -a 256 $(OUTDIR)/os/$(FINCH_OS_BASENAME) | cut -d ' ' -f 1` @@ -54,7 +54,7 @@ arch-test: @if [ $(SUPPORTED_ARCH) != "true" ]; then echo "Unsupported architecture: $(ARCH)"; exit "1"; fi .PHONY: all -all: arch-test finch networks.yaml config.yaml lima-and-qemu finch-core finch.yaml +all: arch-test finch finch-core finch.yaml networks.yaml config.yaml lima-and-qemu .PHONY: finch-core finch-core: @@ -62,11 +62,10 @@ finch-core: FINCH_OS_x86_URL="$(FINCH_OS_x86_URL)" \ FINCH_OS_AARCH64_URL="$(FINCH_OS_AARCH64_URL)" \ VDE_TEMP_PREFIX=$(CORE_VDE_PREFIX) \ - $(MAKE) all lima + $(MAKE) mkdir -p _output cd deps/finch-core/_output && tar c * | tar Cvx $(OUTDIR) - cd deps/finch-core/src/lima/_output && tar c * | tar Cvx $(OUTDIR)/lima rm -rf $(OUTDIR)/lima-template .PHONY: lima-and-qemu diff --git a/deps/finch-core b/deps/finch-core index fa1a20e17..ffd3d3107 160000 --- a/deps/finch-core +++ b/deps/finch-core @@ -1 +1 @@ -Subproject commit fa1a20e17e0f670ff9d34c2508a660f4d15ba91b +Subproject commit ffd3d31071d1e04490b11122fbe329b54171a86e From 811e6ae29c5aafde38b88085dea673df5afbadd8 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 15:28:15 -0400 Subject: [PATCH 10/19] fix tests Signed-off-by: Justin Alvarez --- .../virtualization_framework_rosetta_test.go | 54 ++++++++++--------- e2e/vm/vm_test.go | 4 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/e2e/vm/virtualization_framework_rosetta_test.go b/e2e/vm/virtualization_framework_rosetta_test.go index 32c51e9a5..1d2934d35 100644 --- a/e2e/vm/virtualization_framework_rosetta_test.go +++ b/e2e/vm/virtualization_framework_rosetta_test.go @@ -20,40 +20,41 @@ import ( "github.com/runfinch/finch/pkg/config" ) -var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed bool) { - ginkgo.Describe("Virtualization framework", ginkgo.Ordered, func() { - var limaConfigFilePath string +var reset = func(o *option.Option, installed bool) { + var limaConfigFilePath string - supportsVz, supportsVzErr := config.SupportsVirtualizationFramework(finch_cmd.NewExecCmdCreator()) - gomega.Expect(supportsVzErr).ShouldNot(gomega.HaveOccurred()) + origFinchCfg := readFile(finchConfigFilePath) + limaConfigFilePath = defaultLimaConfigFilePath + if installed { + path, err := exec.LookPath(e2e.InstalledTestSubject) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + realFinchPath, err := filepath.EvalSymlinks(path) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + } + origLimaCfg := readFile(limaConfigFilePath) - ginkgo.BeforeEach(func() { - origFinchCfg := readFile(finchConfigFilePath) - limaConfigFilePath = defaultLimaConfigFilePath - if installed { - path, err := exec.LookPath(e2e.InstalledTestSubject) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - realFinchPath, err := filepath.EvalSymlinks(path) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") - } - origLimaCfg := readFile(limaConfigFilePath) + command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + ginkgo.DeferCleanup(func() { + writeFile(finchConfigFilePath, origFinchCfg) + writeFile(limaConfigFilePath, origLimaCfg) + }) +} - ginkgo.DeferCleanup(func() { - writeFile(finchConfigFilePath, origFinchCfg) - writeFile(limaConfigFilePath, origLimaCfg) - }) - }) +var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed bool) { + ginkgo.Describe("Virtualization framework", ginkgo.Ordered, func() { + supportsVz, supportsVzErr := config.SupportsVirtualizationFramework(finch_cmd.NewExecCmdCreator()) + gomega.Expect(supportsVzErr).ShouldNot(gomega.HaveOccurred()) - ginkgo.FDescribe("Virtualization framework", ginkgo.Ordered, func() { - ginkgo.BeforeEach(func() { + ginkgo.Describe("Virtualization framework", ginkgo.Ordered, func() { + ginkgo.BeforeAll(func() { if !supportsVz { ginkgo.Skip("Skipping because system does not support Virtualization.framework") } + reset(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: false")) initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) @@ -66,11 +67,12 @@ var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed boo }) ginkgo.Describe("Virtualization framework and Rosetta", ginkgo.Ordered, func() { - ginkgo.BeforeEach(func() { + ginkgo.BeforeAll(func() { if !supportsVz || runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { ginkgo.Skip("Skipping because system does not support Rosetta") } + reset(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index 5b9cd4def..084d6709c 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -34,8 +34,8 @@ func TestVM(t *testing.T) { }, func(bytes []byte) {}) ginkgo.SynchronizedAfterSuite(func() { - command.New(o, "vm", "stop").WithTimeoutInSeconds(90).Run() - command.New(o, "vm", "remove").WithTimeoutInSeconds(60).Run() + command.New(o, "vm", "stop", "-f").WithTimeoutInSeconds(90).Run() + command.New(o, "vm", "remove", "-f").WithTimeoutInSeconds(60).Run() }, func() {}) ginkgo.Describe("", func() { From 8b254b13094a142a131dd9cbae45055be45d9775 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 15:31:17 -0400 Subject: [PATCH 11/19] Fix linting error Signed-off-by: Justin Alvarez --- pkg/config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/config/config.go b/pkg/config/config.go index 174c35d9d..e704f8ba1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -153,6 +153,7 @@ func Load(fs afero.Fs, cfgPath string, log flog.Logger, systemDeps LoadSystemDep return defCfg, nil } +// SupportsVirtualizationFramework checks if the user's system supports Virtualization.framework. func SupportsVirtualizationFramework(cmdCreator command.Creator) (bool, error) { cmd := cmdCreator.Create("sw_vers", "-productVersion") out, err := cmd.Output() From 2db4ff0a71cac3e7beadbdd859280d2107ca9c12 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 16:13:05 -0400 Subject: [PATCH 12/19] update minimum macOS version for VZ to 13 increase test timeout Signed-off-by: Justin Alvarez --- e2e/vm/virtualization_framework_rosetta_test.go | 4 ++-- pkg/config/config.go | 2 +- pkg/config/lima_config_applier_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e/vm/virtualization_framework_rosetta_test.go b/e2e/vm/virtualization_framework_rosetta_test.go index 1d2934d35..d5c7d824b 100644 --- a/e2e/vm/virtualization_framework_rosetta_test.go +++ b/e2e/vm/virtualization_framework_rosetta_test.go @@ -56,7 +56,7 @@ var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed boo reset(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: false")) - initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() + initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(180).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) }) @@ -74,7 +74,7 @@ var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed boo reset(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) - initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() + initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(180).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) }) diff --git a/pkg/config/config.go b/pkg/config/config.go index e704f8ba1..ce1835b3b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -171,7 +171,7 @@ func SupportsVirtualizationFramework(cmdCreator command.Creator) (bool, error) { return false, fmt.Errorf("failed to parse split sw_vers output (%s) into int: %w", splitVer[0], err) } - if majorVersionInt >= 11 { + if majorVersionInt >= 13 { return true, nil } diff --git a/pkg/config/lima_config_applier_test.go b/pkg/config/lima_config_applier_test.go index 420496ddf..20d26e063 100644 --- a/pkg/config/lima_config_applier_test.go +++ b/pkg/config/lima_config_applier_test.go @@ -54,7 +54,7 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) - cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) + cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) }, postRunCheck: func(t *testing.T, fs afero.Fs) { @@ -242,7 +242,7 @@ dnf install -y --setopt=install_weak_deps=False qemu-user-static-aarch64 qemu-us ) { err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) require.NoError(t, err) - cmd.EXPECT().Output().Return([]byte("12.6.1"), nil) + cmd.EXPECT().Output().Return([]byte("13.0.0"), nil) creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd) }, postRunCheck: func(t *testing.T, fs afero.Fs) { From 5fd49952bcae84fdff38a37f29899a72b577e252 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 16:56:36 -0400 Subject: [PATCH 13/19] update tests Signed-off-by: Justin Alvarez --- e2e/vm/config_test.go | 56 ++++--------------- .../virtualization_framework_rosetta_test.go | 30 +--------- e2e/vm/vm_test.go | 28 ++++++++++ 3 files changed, 42 insertions(+), 72 deletions(-) diff --git a/e2e/vm/config_test.go b/e2e/vm/config_test.go index 1afdebb04..4e680abf5 100644 --- a/e2e/vm/config_test.go +++ b/e2e/vm/config_test.go @@ -7,8 +7,8 @@ import ( "errors" "io/fs" "os" - "os/exec" "path/filepath" + "runtime" "github.com/lima-vm/lima/pkg/limayaml" "github.com/onsi/ginkgo/v2" @@ -19,7 +19,8 @@ import ( "github.com/xorcare/pointer" "gopkg.in/yaml.v3" - "github.com/runfinch/finch/e2e" + finch_cmd "github.com/runfinch/finch/pkg/command" + "github.com/runfinch/finch/pkg/config" ) var finchConfigFilePath = os.Getenv("HOME") + "/.finch/finch.yaml" @@ -64,24 +65,7 @@ var testConfig = func(o *option.Option, installed bool) { ginkgo.Describe("Config", ginkgo.Serial, func() { var limaConfigFilePath string ginkgo.BeforeEach(func() { - origFinchCfg := readFile(finchConfigFilePath) - limaConfigFilePath = defaultLimaConfigFilePath - if installed { - path, err := exec.LookPath(e2e.InstalledTestSubject) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - realFinchPath, err := filepath.EvalSymlinks(path) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") - } - origLimaCfg := readFile(limaConfigFilePath) - - ginkgo.DeferCleanup(func() { - writeFile(finchConfigFilePath, origFinchCfg) - writeFile(limaConfigFilePath, origLimaCfg) - - command.New(o, virtualMachineRootCmd, "stop").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(120).Run() - }) + limaConfigFilePath = resetVM(o, installed) }) ginkgo.It("updates config values when a config file is present", func() { @@ -187,31 +171,15 @@ additional_directories: }) ginkgo.Describe("Config (after init)", ginkgo.Serial, func() { - var limaConfigFilePath string - - ginkgo.BeforeEach(func() { - origFinchCfg := readFile(finchConfigFilePath) - limaConfigFilePath = defaultLimaConfigFilePath - if installed { - path, err := exec.LookPath(e2e.InstalledTestSubject) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - realFinchPath, err := filepath.EvalSymlinks(path) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + ginkgo.It("updates init-only config values when values are changed after init", func() { + supportsVz, supportsVzErr := config.SupportsVirtualizationFramework(finch_cmd.NewExecCmdCreator()) + gomega.Expect(supportsVzErr).ShouldNot(gomega.HaveOccurred()) + if !supportsVz || runtime.GOOS != "darwin" { + ginkgo.Skip("Skipping because existing init only configuration options require Virtualization.framework support to test") } - origLimaCfg := readFile(limaConfigFilePath) - - command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - - ginkgo.DeferCleanup(func() { - writeFile(finchConfigFilePath, origFinchCfg) - writeFile(limaConfigFilePath, origLimaCfg) - }) - }) - ginkgo.It("updates init-only config values when values are changed after init", func() { - writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) + limaConfigFilePath := resetVM(o, installed) + writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: false")) initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(120).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) @@ -226,7 +194,7 @@ additional_directories: gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("4GiB")) gomega.Expect(*limaCfg.VMType).Should(gomega.Equal("vz")) gomega.Expect(limaCfg.Rosetta.Enabled).Should(gomega.Equal(false)) - gomega.Expect(limaCfg.Rosetta.BinFmt).Should(gomega.Equal(true)) + gomega.Expect(limaCfg.Rosetta.BinFmt).Should(gomega.Equal(false)) }) }) } diff --git a/e2e/vm/virtualization_framework_rosetta_test.go b/e2e/vm/virtualization_framework_rosetta_test.go index d5c7d824b..4d2361a28 100644 --- a/e2e/vm/virtualization_framework_rosetta_test.go +++ b/e2e/vm/virtualization_framework_rosetta_test.go @@ -4,8 +4,6 @@ package vm import ( - "os/exec" - "path/filepath" "runtime" "github.com/onsi/ginkgo/v2" @@ -15,34 +13,10 @@ import ( "github.com/runfinch/common-tests/option" "github.com/runfinch/common-tests/tests" - "github.com/runfinch/finch/e2e" finch_cmd "github.com/runfinch/finch/pkg/command" "github.com/runfinch/finch/pkg/config" ) -var reset = func(o *option.Option, installed bool) { - var limaConfigFilePath string - - origFinchCfg := readFile(finchConfigFilePath) - limaConfigFilePath = defaultLimaConfigFilePath - if installed { - path, err := exec.LookPath(e2e.InstalledTestSubject) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - realFinchPath, err := filepath.EvalSymlinks(path) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") - } - origLimaCfg := readFile(limaConfigFilePath) - - command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - - ginkgo.DeferCleanup(func() { - writeFile(finchConfigFilePath, origFinchCfg) - writeFile(limaConfigFilePath, origLimaCfg) - }) -} - var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed bool) { ginkgo.Describe("Virtualization framework", ginkgo.Ordered, func() { supportsVz, supportsVzErr := config.SupportsVirtualizationFramework(finch_cmd.NewExecCmdCreator()) @@ -54,7 +28,7 @@ var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed boo ginkgo.Skip("Skipping because system does not support Virtualization.framework") } - reset(o, installed) + resetVM(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: false")) initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(180).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) @@ -72,7 +46,7 @@ var testVirtualizationFrameworkAndRosetta = func(o *option.Option, installed boo ginkgo.Skip("Skipping because system does not support Rosetta") } - reset(o, installed) + resetVM(o, installed) writeFile(finchConfigFilePath, []byte("memory: 4GiB\ncpus: 6\nvmType: vz\nrosetta: true")) initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(180).Run() gomega.Expect(initCmdSession).Should(gexec.Exit(0)) diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index 084d6709c..7b96c5f14 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -5,12 +5,15 @@ package vm import ( + "os/exec" + "path/filepath" "testing" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/runfinch/common-tests/command" + "github.com/runfinch/common-tests/option" "github.com/runfinch/finch/e2e" ) @@ -49,3 +52,28 @@ func TestVM(t *testing.T) { gomega.RegisterFailHandler(ginkgo.Fail) ginkgo.RunSpecs(t, description) } + +var resetVM = func(o *option.Option, installed bool) string { + var limaConfigFilePath string + + origFinchCfg := readFile(finchConfigFilePath) + limaConfigFilePath = defaultLimaConfigFilePath + if installed { + path, err := exec.LookPath(e2e.InstalledTestSubject) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + realFinchPath, err := filepath.EvalSymlinks(path) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + } + origLimaCfg := readFile(limaConfigFilePath) + + command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + + ginkgo.DeferCleanup(func() { + writeFile(finchConfigFilePath, origFinchCfg) + writeFile(limaConfigFilePath, origLimaCfg) + }) + + return limaConfigFilePath +} From c26e6df4e960e6dcfe75df63c9b889f0f83f8064 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 17:15:20 -0400 Subject: [PATCH 14/19] fix over zealous refactoring of test Signed-off-by: Justin Alvarez --- e2e/vm/config_test.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/e2e/vm/config_test.go b/e2e/vm/config_test.go index 4e680abf5..518d533c6 100644 --- a/e2e/vm/config_test.go +++ b/e2e/vm/config_test.go @@ -7,6 +7,7 @@ import ( "errors" "io/fs" "os" + "os/exec" "path/filepath" "runtime" @@ -19,6 +20,7 @@ import ( "github.com/xorcare/pointer" "gopkg.in/yaml.v3" + "github.com/runfinch/finch/e2e" finch_cmd "github.com/runfinch/finch/pkg/command" "github.com/runfinch/finch/pkg/config" ) @@ -65,7 +67,24 @@ var testConfig = func(o *option.Option, installed bool) { ginkgo.Describe("Config", ginkgo.Serial, func() { var limaConfigFilePath string ginkgo.BeforeEach(func() { - limaConfigFilePath = resetVM(o, installed) + origFinchCfg := readFile(finchConfigFilePath) + limaConfigFilePath = defaultLimaConfigFilePath + if installed { + path, err := exec.LookPath(e2e.InstalledTestSubject) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + realFinchPath, err := filepath.EvalSymlinks(path) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + limaConfigFilePath = filepath.Join(realFinchPath, "../../lima/data/_config/override.yaml") + } + origLimaCfg := readFile(limaConfigFilePath) + + ginkgo.DeferCleanup(func() { + writeFile(finchConfigFilePath, origFinchCfg) + writeFile(limaConfigFilePath, origLimaCfg) + + command.New(o, virtualMachineRootCmd, "stop").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(120).Run() + }) }) ginkgo.It("updates config values when a config file is present", func() { From 0417edfc14f9120cb519728cadb7891fa16828d2 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 18:39:55 -0400 Subject: [PATCH 15/19] relaunch vm after reset Signed-off-by: Justin Alvarez --- e2e/vm/vm_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index 7b96c5f14..13efed35b 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -73,6 +73,9 @@ var resetVM = func(o *option.Option, installed bool) string { ginkgo.DeferCleanup(func() { writeFile(finchConfigFilePath, origFinchCfg) writeFile(limaConfigFilePath, origLimaCfg) + command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "init").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() }) return limaConfigFilePath From 5d3f16a22c9401672d4503ced7b2e17c24801b80 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 20:00:32 -0400 Subject: [PATCH 16/19] Add step to install Rosetta 2 Signed-off-by: Justin Alvarez --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 919dc155e..00101153c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -102,6 +102,8 @@ jobs: if pgrep '^socket_vmnet'; then sudo pkill '^socket_vmnet' fi + - name: Install Rosetta 2 + run: echo "A" | softwareupdate --install-rosetta - run: brew install go lz4 automake autoconf libtool - name: Build project run: | From 4da7f1a45e62e4898b32c2cc4140a67b5ca90ea0 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 20:00:58 -0400 Subject: [PATCH 17/19] Fix docs Signed-off-by: Justin Alvarez --- pkg/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index ce1835b3b..93d670b4f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -42,7 +42,7 @@ type Finch struct { // VMType sets which technology to use for Finch's VM. // Currently supports `qemu` and `vz` (Virtualization.framework). // Also sets mountType to "virtiofs", instead of the default "reverse-sshfs" - // Requires macOS macOS 11.0 or later. + // Requires macOS 13.0 or later. // This setting will only be applied on vm init. VMType *limayaml.VMType `yaml:"vmType,omitempty"` // Use Rosetta 2 when available. Forces vmType to "vz" (Virtualization.framework) if set to `true`. From 15150b300dc5d36bcdc1607baeec97b879bf1c54 Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Mon, 27 Mar 2023 20:05:42 -0400 Subject: [PATCH 18/19] ignore exit code for Rosetta install Signed-off-by: Justin Alvarez --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 00101153c..89b773aa8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -103,7 +103,7 @@ jobs: sudo pkill '^socket_vmnet' fi - name: Install Rosetta 2 - run: echo "A" | softwareupdate --install-rosetta + run: echo "A" | softwareupdate --install-rosetta || true - run: brew install go lz4 automake autoconf libtool - name: Build project run: | From 7727e739b662299df2ee07dc7d9561458a31cc1f Mon Sep 17 00:00:00 2001 From: Justin Alvarez Date: Tue, 28 Mar 2023 11:55:50 -0400 Subject: [PATCH 19/19] increase test timeouts Signed-off-by: Justin Alvarez --- e2e/vm/config_test.go | 2 +- e2e/vm/vm_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/vm/config_test.go b/e2e/vm/config_test.go index 518d533c6..cfc1e4746 100644 --- a/e2e/vm/config_test.go +++ b/e2e/vm/config_test.go @@ -83,7 +83,7 @@ var testConfig = func(o *option.Option, installed bool) { writeFile(limaConfigFilePath, origLimaCfg) command.New(o, virtualMachineRootCmd, "stop").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(120).Run() + command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(600).Run() }) }) diff --git a/e2e/vm/vm_test.go b/e2e/vm/vm_test.go index 13efed35b..b4843c46d 100644 --- a/e2e/vm/vm_test.go +++ b/e2e/vm/vm_test.go @@ -75,7 +75,7 @@ var resetVM = func(o *option.Option, installed bool) string { writeFile(limaConfigFilePath, origLimaCfg) command.New(o, virtualMachineRootCmd, "stop", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() command.New(o, virtualMachineRootCmd, "remove", "-f").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() - command.New(o, virtualMachineRootCmd, "init").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() + command.New(o, virtualMachineRootCmd, "init").WithoutCheckingExitCode().WithTimeoutInSeconds(600).Run() }) return limaConfigFilePath