From b76d0c5aee42e02eac8a447870a8c5d6ef7a5ec3 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 23 May 2025 14:44:31 +0900 Subject: [PATCH 01/91] info: silence "guest agent binary could not be found" Fix issue 3570 pkg/limainfo expects `fs.ErrNoExist` when the guest agent binary could not be found Signed-off-by: Akihiro Suda --- pkg/usrlocalsharelima/usrlocalsharelima.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go index 4ef008c1b17..8ad5cf66896 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocalsharelima/usrlocalsharelima.go @@ -115,7 +115,7 @@ func GuestAgentBinary(ostype limayaml.OS, arch limayaml.Arch) (string, error) { res, err := chooseGABinary([]string{comp, uncomp}) if err != nil { logrus.Debug(err) - return "", fmt.Errorf("guest agent binary could not be found for %s-%s (Hint: try installing `lima-additional-guestagents` package)", ostype, arch) + return "", fmt.Errorf("guest agent binary could not be found for %s-%s: %w (Hint: try installing `lima-additional-guestagents` package)", ostype, arch, err) } return res, nil } From 835263dc5ab077ae43e8f9b09d223baccb6adad4 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 23 May 2025 16:02:54 +0900 Subject: [PATCH 02/91] CI: release: avoid running apt-get twice Signed-off-by: Akihiro Suda --- .github/workflows/release.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e3ce40cabba..f571f7cb720 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,14 +68,10 @@ jobs: - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: 1.24.x - - name: Install gcc-x86-64-linux-gnu + - name: Install gcc run: | sudo apt-get update - sudo apt-get install -y gcc-x86-64-linux-gnu - - name: Install gcc-aarch64-linux-gnu - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu + sudo apt-get install -y gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu - name: "Compile binaries" run: make artifacts-linux - name: "Make misc artifacts" From df63555964f65c1c3f0dacbd16e32f76bcc32c19 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Sun, 25 May 2025 13:34:11 -0700 Subject: [PATCH 03/91] Allow dotted instance names in `limactl start` limatmpl.Read() no longer returns an empty template when called with an instance name because it now needs to be able to read any kind of file via the template:// mechanism. This commit changes the callers to no longer call limatmpl.Read() with an instance name. This also allows simplification of the locator type selection: every locator that is not a URL can now be treated as a local file. Signed-off-by: Jan Dubois --- cmd/limactl/edit.go | 30 +++++++----------- cmd/limactl/start.go | 49 ++++++++++++++++------------- pkg/limatmpl/locator.go | 51 ++++++++++-------------------- pkg/limatmpl/locator_test.go | 61 ++++++++++++++++++++++++++++++++++++ pkg/store/store.go | 16 +++++++++- pkg/store/store_test.go | 61 ++++++++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+), 74 deletions(-) create mode 100644 pkg/store/store_test.go diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index b24e61416a5..a411df71863 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -13,7 +13,6 @@ import ( "github.com/lima-vm/lima/cmd/limactl/editflags" "github.com/lima-vm/lima/pkg/editutil" "github.com/lima-vm/lima/pkg/instance" - "github.com/lima-vm/lima/pkg/limatmpl" "github.com/lima-vm/lima/pkg/limayaml" networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/store" @@ -46,33 +45,28 @@ func editAction(cmd *cobra.Command, args []string) error { var filePath string var err error var inst *store.Instance - switch { - case limatmpl.SeemsYAMLPath(arg): - // absolute path is required for `limayaml.Validate` - filePath, err = filepath.Abs(arg) - if err != nil { - return err - } - default: - var instName string - if arg != "" { - instName = arg - } else { - instName = DefaultInstanceName - } - inst, err = store.Inspect(instName) + if arg == "" { + arg = DefaultInstanceName + } + if err := store.ValidateInstName(arg); err == nil { + inst, err = store.Inspect(arg) if err != nil { if errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("instance %q not found", instName) + return fmt.Errorf("instance %q not found", arg) } return err } - if inst.Status == store.StatusRunning { return errors.New("cannot edit a running instance") } filePath = filepath.Join(inst.Dir, filenames.LimaYAML) + } else { + // absolute path is required for `limayaml.Validate` + filePath, err = filepath.Abs(arg) + if err != nil { + return err + } } yContent, err := os.ReadFile(filePath) diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index c09aaa85a9c..5187de6005e 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -7,12 +7,10 @@ import ( "errors" "fmt" "os" - "path" "path/filepath" "runtime" "strings" - "github.com/containerd/containerd/identifiers" "github.com/lima-vm/lima/cmd/limactl/editflags" "github.com/lima-vm/lima/pkg/editutil" "github.com/lima-vm/lima/pkg/instance" @@ -118,8 +116,13 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (* if err != nil { return nil, err } - if isTemplateURL, templateURL := limatmpl.SeemsTemplateURL(arg); isTemplateURL { - templateName := path.Join(templateURL.Host, templateURL.Path) + if name != "" { + err := store.ValidateInstName(name) + if err != nil { + return nil, err + } + } + if isTemplateURL, templateName := limatmpl.SeemsTemplateURL(arg); isTemplateURL { switch templateName { case "experimental/vz": logrus.Warn("template://experimental/vz was merged into the default template in Lima v1.0. See also .") @@ -144,35 +147,25 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (* // see if the tty was set explicitly or not ttySet := cmd.Flags().Changed("tty") if ttySet && tty { - return nil, errors.New("cannot use --tty=true and read template from stdin together") + return nil, errors.New("cannot use --tty=true when reading template from stdin") } tty = false } - tmpl, err := limatmpl.Read(cmd.Context(), name, arg) - if err != nil { - return nil, err - } - if len(tmpl.Bytes) > 0 { - if createOnly { - if _, err := store.Inspect(tmpl.Name); err == nil { - return nil, fmt.Errorf("instance %q already exists", tmpl.Name) - } - } - } else { + var tmpl *limatmpl.Template + if err := store.ValidateInstName(arg); arg == "" || err == nil { + tmpl = &limatmpl.Template{Name: name} if arg == "" { - if tmpl.Name == "" { + if name == "" { tmpl.Name = DefaultInstanceName } } else { logrus.Debugf("interpreting argument %q as an instance name", arg) - if tmpl.Name != "" && tmpl.Name != arg { + if name != "" && name != arg { return nil, fmt.Errorf("instance name %q and CLI flag --name=%q cannot be specified together", arg, tmpl.Name) } tmpl.Name = arg } - if err := identifiers.Validate(tmpl.Name); err != nil { - return nil, fmt.Errorf("argument must be either an instance name, a YAML file path, or a URL, got %q: %w", tmpl.Name, err) - } + // store.Inspect() will validate the template name (in case it has been set to arg) inst, err := store.Inspect(tmpl.Name) if err == nil { if createOnly { @@ -204,7 +197,21 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (* if err != nil { return nil, err } + } else { + tmpl, err = limatmpl.Read(cmd.Context(), name, arg) + if err != nil { + return nil, err + } + if createOnly { + // store.Inspect() will also validate the instance name + if _, err := store.Inspect(tmpl.Name); err == nil { + return nil, fmt.Errorf("instance %q already exists", tmpl.Name) + } + } else if err := store.ValidateInstName(tmpl.Name); err != nil { + return nil, err + } } + if err := tmpl.Embed(cmd.Context(), true, true); err != nil { return nil, err } diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go index 3f2f2674d73..218311af100 100644 --- a/pkg/limatmpl/locator.go +++ b/pkg/limatmpl/locator.go @@ -15,7 +15,6 @@ import ( "regexp" "runtime" "strings" - "unicode" "github.com/containerd/containerd/identifiers" "github.com/lima-vm/lima/pkg/ioutilx" @@ -37,10 +36,9 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { return tmpl, nil } - isTemplateURL, templateURL := SeemsTemplateURL(locator) + isTemplateURL, templateName := SeemsTemplateURL(locator) switch { case isTemplateURL: - templateName := path.Join(templateURL.Host, templateURL.Path) logrus.Debugf("interpreting argument %q as a template name %q", locator, templateName) if tmpl.Name == "" { // e.g., templateName = "deprecated/centos-7.yaml" , tmpl.Name = "centos-7" @@ -95,7 +93,12 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case SeemsFilePath(locator): + case locator == "-": + tmpl.Bytes, err = io.ReadAll(os.Stdin) + if err != nil { + return nil, fmt.Errorf("unexpected error reading stdin: %w", err) + } + default: if tmpl.Name == "" { tmpl.Name, err = InstNameFromYAMLPath(locator) if err != nil { @@ -116,11 +119,6 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case locator == "-": - tmpl.Bytes, err = io.ReadAll(os.Stdin) - if err != nil { - return nil, fmt.Errorf("unexpected error reading stdin: %w", err) - } } // The only reason not to call tmpl.UseAbsLocators() here is that `limactl tmpl copy --verbatim …` // should create an unmodified copy of the template. @@ -233,14 +231,20 @@ func InstNameFromImageURL(locator, imageArch string) string { return name } -func SeemsTemplateURL(arg string) (bool, *url.URL) { +// SeemsTemplateURL returns true if the arg is a URL using the template scheme. +// When it returns true, it also returns the template name. +func SeemsTemplateURL(arg string) (isTemplate bool, templateName string) { u, err := url.Parse(arg) if err != nil { - return false, u + return false, "" } - return u.Scheme == "template", u + if u.Scheme == "template" { + return true, path.Join(u.Host, u.Path) + } + return false, "" } +// SeemsHTTPURL returns true if the arg is a URL using the http or https scheme. func SeemsHTTPURL(arg string) bool { u, err := url.Parse(arg) if err != nil { @@ -252,6 +256,7 @@ func SeemsHTTPURL(arg string) bool { return true } +// SeemsFileURL returns true if the arg is a URL using the file scheme. func SeemsFileURL(arg string) bool { u, err := url.Parse(arg) if err != nil { @@ -260,28 +265,6 @@ func SeemsFileURL(arg string) bool { return u.Scheme == "file" } -func SeemsYAMLPath(arg string) bool { - if strings.Contains(arg, "/") { - return true - } - lower := strings.ToLower(arg) - return strings.HasSuffix(lower, ".yml") || strings.HasSuffix(lower, ".yaml") -} - -// SeemsFilePath returns true if arg either contains a path separator or has a file extension that -// does not start with a digit. `my.yaml` is a file path, `ubuntu-20.10` is not. -func SeemsFilePath(arg string) bool { - // Single-letter schemes will be drive names on Windows, e.g. "c:/foo.yaml" - if u, err := url.Parse(arg); err == nil && len(u.Scheme) > 1 { - return false - } - if strings.ContainsRune(arg, '/') || strings.ContainsRune(arg, filepath.Separator) { - return true - } - ext := filepath.Ext(arg) - return len(ext) > 1 && !unicode.IsDigit(rune(ext[1])) -} - func InstNameFromURL(urlStr string) (string, error) { u, err := url.Parse(urlStr) if err != nil { diff --git a/pkg/limatmpl/locator_test.go b/pkg/limatmpl/locator_test.go index dcf510c2bc0..6c2c03d838a 100644 --- a/pkg/limatmpl/locator_test.go +++ b/pkg/limatmpl/locator_test.go @@ -61,3 +61,64 @@ func TestInstNameFromImageURL(t *testing.T) { assert.Equal(t, name, "rocky-8-9.10") }) } + +func TestSeemsTemplateURL(t *testing.T) { + arg := "template://foo/bar" + t.Run(arg, func(t *testing.T) { + is, name := limatmpl.SeemsTemplateURL(arg) + assert.Equal(t, is, true) + assert.Equal(t, name, "foo/bar") + }) + notTemplateURLs := []string{ + "file:///foo", + "http://foo", + "https://foo", + "foo", + } + for _, arg := range notTemplateURLs { + t.Run(arg, func(t *testing.T) { + is, _ := limatmpl.SeemsTemplateURL(arg) + assert.Equal(t, is, false) + }) + } +} + +func TestSeemsHTTPURL(t *testing.T) { + httpURLs := []string{ + "http://foo/", + "https://foo/", + } + for _, arg := range httpURLs { + t.Run(arg, func(t *testing.T) { + assert.Equal(t, limatmpl.SeemsHTTPURL(arg), true) + }) + } + notHTTPURLs := []string{ + "file:///foo", + "template://foo", + "foo", + } + for _, arg := range notHTTPURLs { + t.Run(arg, func(t *testing.T) { + assert.Equal(t, limatmpl.SeemsHTTPURL(arg), false) + }) + } +} + +func TestSeemsFileURL(t *testing.T) { + arg := "file:///foo" + t.Run(arg, func(t *testing.T) { + assert.Equal(t, limatmpl.SeemsFileURL(arg), true) + }) + notFileURLs := []string{ + "http://foo", + "https://foo", + "template://foo", + "foo", + } + for _, arg := range notFileURLs { + t.Run(arg, func(t *testing.T) { + assert.Equal(t, limatmpl.SeemsFileURL(arg), false) + }) + } +} diff --git a/pkg/store/store.go b/pkg/store/store.go index 60732d10e8e..58669dd753a 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -5,6 +5,7 @@ package store import ( "errors" + "fmt" "os" "path/filepath" "strings" @@ -45,6 +46,19 @@ func Validate() error { return nil } +// ValidateInstName checks if the name is a valid instance name. For this it needs to +// be a valid identifier, and not end in .yml or .yaml (case insensitively). +func ValidateInstName(name string) error { + if err := identifiers.Validate(name); err != nil { + return fmt.Errorf("instance name %q is not a valid identifier: %w", name, err) + } + lower := strings.ToLower(name) + if strings.HasSuffix(lower, ".yml") || strings.HasSuffix(lower, ".yaml") { + return fmt.Errorf("instance name %q must not end with .yml or .yaml suffix", name) + } + return nil +} + // Instances returns the names of the instances under LimaDir. func Instances() ([]string, error) { limaDir, err := dirnames.LimaDir() @@ -93,7 +107,7 @@ func Disks() ([]string, error) { // InstanceDir returns the instance dir. // InstanceDir does not check whether the instance exists. func InstanceDir(name string) (string, error) { - if err := identifiers.Validate(name); err != nil { + if err := ValidateInstName(name); err != nil { return "", err } limaDir, err := dirnames.LimaDir() diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go new file mode 100644 index 00000000000..4dfe1eea87b --- /dev/null +++ b/pkg/store/store_test.go @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package store + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestValidateInstName(t *testing.T) { + instNames := []string{ + "default", + "Ubuntu-20.04", + "example.com", + "under_score", + "1-2_3.4", + "yml", + "yaml", + "foo.yaml.com", + } + for _, arg := range instNames { + t.Run(arg, func(t *testing.T) { + err := ValidateInstName(arg) + assert.NilError(t, err) + }) + } + invalidIdentifiers := []string{ + "", + "my/instance", + "my\\instance", + "c:default", + "dot.", + ".dot", + "dot..dot", + "underscore_", + "_underscore", + "underscore__underscore", + "dash-", + "-dash", + "dash--dash", + } + for _, arg := range invalidIdentifiers { + t.Run(arg, func(t *testing.T) { + err := ValidateInstName(arg) + assert.ErrorContains(t, err, "not a valid identifier") + }) + } + yamlNames := []string{ + "default.yaml", + "MY.YAML", + "My.YmL", + } + for _, arg := range yamlNames { + t.Run(arg, func(t *testing.T) { + err := ValidateInstName(arg) + assert.ErrorContains(t, err, "must not end with .y") + }) + } +} From 54107efbd7383c835511e97f2292ac0a9b855fe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 01:46:54 +0000 Subject: [PATCH 04/91] build(deps): bump github.com/google/yamlfmt from 0.16.0 to 0.17.0 Bumps [github.com/google/yamlfmt](https://github.com/google/yamlfmt) from 0.16.0 to 0.17.0. - [Release notes](https://github.com/google/yamlfmt/releases) - [Changelog](https://github.com/google/yamlfmt/blob/main/.goreleaser.yaml) - [Commits](https://github.com/google/yamlfmt/compare/v0.16.0...v0.17.0) --- updated-dependencies: - dependency-name: github.com/google/yamlfmt dependency-version: 0.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 3 +-- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ad48c336ac8..c5cd417195f 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/foxcpp/go-mockdns v1.1.0 github.com/goccy/go-yaml v1.17.1 github.com/google/go-cmp v0.7.0 - github.com/google/yamlfmt v0.16.0 + github.com/google/yamlfmt v0.17.0 github.com/invopop/jsonschema v0.13.0 github.com/lima-vm/go-qcow2reader v0.6.0 github.com/lima-vm/sshocker v0.3.8 // gomodjail:unconfined @@ -62,7 +62,6 @@ require ( github.com/alecthomas/participle/v2 v2.1.4 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect - github.com/braydonk/yaml v0.9.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect diff --git a/go.sum b/go.sum index 30c2aadfe7c..fe2947ae966 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,6 @@ github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e h1:IdMhFPEfTZQU971tI github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ= github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/braydonk/yaml v0.9.0 h1:ewGMrVmEVpsm3VwXQDR388sLg5+aQ8Yihp6/hc4m+h4= -github.com/braydonk/yaml v0.9.0/go.mod h1:hcm3h581tudlirk8XEUPDBAimBPbmnL0Y45hCRl47N4= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI= @@ -129,8 +127,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/yamlfmt v0.16.0 h1:5auoxqdx2CxOb022XGBElFFVH8uE/lAJDCWKRMq4mT8= -github.com/google/yamlfmt v0.16.0/go.mod h1:/fF8jQmFopG3InQoWYG3gTORPXqLwNkcBqAT4UA4ab0= +github.com/google/yamlfmt v0.17.0 h1:/tdp01rIlvLz3LgJ2NtMLnqgAadZm33P7GcPU680b+w= +github.com/google/yamlfmt v0.17.0/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= From 7a557f6303181d9008eb293f05b59632141eef17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 01:47:02 +0000 Subject: [PATCH 05/91] build(deps): bump github.com/santhosh-tekuri/jsonschema/v6 Bumps [github.com/santhosh-tekuri/jsonschema/v6](https://github.com/santhosh-tekuri/jsonschema) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/santhosh-tekuri/jsonschema/releases) - [Commits](https://github.com/santhosh-tekuri/jsonschema/compare/v6.0.1...v6.0.2) --- updated-dependencies: - dependency-name: github.com/santhosh-tekuri/jsonschema/v6 dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ad48c336ac8..88238d7e7ca 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/rjeczalik/notify v0.9.3 - github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/sethvargo/go-password v0.3.1 github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af github.com/spf13/cobra v1.9.1 // gomodjail:unconfined diff --git a/go.sum b/go.sum index 30c2aadfe7c..73fa4e33354 100644 --- a/go.sum +++ b/go.sum @@ -241,8 +241,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= From c9fdbd20c09ebc62bf68863e7cd429777ebc56d2 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Sun, 25 May 2025 23:50:01 -0700 Subject: [PATCH 06/91] =?UTF-8?q?Move=20identifierutil=20=E2=86=92=20insta?= =?UTF-8?q?nce/hostname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of the crusade against gratuituous use of `util` package suffix. The package only contains a single `HostnameFromInstName()` function. It can't be moved into the `instance` package because that would create a circular dependency between `instance` and `cidata`. Signed-off-by: Jan Dubois --- pkg/cidata/cidata.go | 4 ++-- pkg/hostagent/hostagent.go | 4 ++-- pkg/identifierutil/identifierutil_test.go | 16 ---------------- .../hostname/hostname.go} | 5 ++++- pkg/instance/hostname/hostname_test.go | 17 +++++++++++++++++ pkg/limayaml/defaults.go | 13 ++++++------- pkg/sshutil/format.go | 14 +++++++------- pkg/store/instance.go | 4 ++-- 8 files changed, 40 insertions(+), 37 deletions(-) delete mode 100644 pkg/identifierutil/identifierutil_test.go rename pkg/{identifierutil/identifierutil.go => instance/hostname/hostname.go} (54%) create mode 100644 pkg/instance/hostname/hostname_test.go diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index a0e4370f31b..0b52e26f7ca 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -21,7 +21,7 @@ import ( "github.com/docker/go-units" "github.com/lima-vm/lima/pkg/debugutil" - "github.com/lima-vm/lima/pkg/identifierutil" + instance "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/localpathutil" @@ -123,7 +123,7 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L Debug: debugutil.Debug, BootScripts: bootScripts, Name: name, - Hostname: identifierutil.HostnameFromInstName(name), // TODO: support customization + Hostname: instance.HostnameFromInstName(name), // TODO: support customization User: *instConfig.User.Name, Comment: *instConfig.User.Comment, Home: *instConfig.User.Home, diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index 36e6d243287..921caa122e5 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -31,7 +31,7 @@ import ( hostagentapi "github.com/lima-vm/lima/pkg/hostagent/api" "github.com/lima-vm/lima/pkg/hostagent/dns" "github.com/lima-vm/lima/pkg/hostagent/events" - "github.com/lima-vm/lima/pkg/identifierutil" + instance "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" @@ -288,7 +288,7 @@ func (a *HostAgent) Run(ctx context.Context) error { if limayaml.FirstUsernetIndex(a.instConfig) == -1 && *a.instConfig.HostResolver.Enabled { hosts := a.instConfig.HostResolver.Hosts hosts["host.lima.internal"] = networks.SlirpGateway - hostname := identifierutil.HostnameFromInstName(a.instName) // TODO: support customization + hostname := instance.HostnameFromInstName(a.instName) // TODO: support customization hosts[hostname] = networks.SlirpIPAddress srvOpts := dns.ServerOptions{ UDPPort: a.udpDNSLocalPort, diff --git a/pkg/identifierutil/identifierutil_test.go b/pkg/identifierutil/identifierutil_test.go deleted file mode 100644 index a8ec2a74936..00000000000 --- a/pkg/identifierutil/identifierutil_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package identifierutil - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestHostnameFromInstName(t *testing.T) { - assert.Equal(t, "lima-default", HostnameFromInstName("default")) - assert.Equal(t, "lima-ubuntu-24-04", HostnameFromInstName("ubuntu-24.04")) - assert.Equal(t, "lima-foo-bar-baz", HostnameFromInstName("foo_bar.baz")) -} diff --git a/pkg/identifierutil/identifierutil.go b/pkg/instance/hostname/hostname.go similarity index 54% rename from pkg/identifierutil/identifierutil.go rename to pkg/instance/hostname/hostname.go index 8f950fce27d..38756c8543d 100644 --- a/pkg/identifierutil/identifierutil.go +++ b/pkg/instance/hostname/hostname.go @@ -1,10 +1,13 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package identifierutil +package instance import "strings" +// HostnameFromInstName generates a hostname from an instance name by prefixing +// it with "lima-" and replacing all dots and underscores with dashes. +// E.g. "my_example.com" becomes "lima-my-example-com". func HostnameFromInstName(instName string) string { s := strings.ReplaceAll(instName, ".", "-") s = strings.ReplaceAll(s, "_", "-") diff --git a/pkg/instance/hostname/hostname_test.go b/pkg/instance/hostname/hostname_test.go new file mode 100644 index 00000000000..69620638566 --- /dev/null +++ b/pkg/instance/hostname/hostname_test.go @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package instance_test + +import ( + "testing" + + instance "github.com/lima-vm/lima/pkg/instance/hostname" + "gotest.tools/v3/assert" +) + +func TestHostnameFromInstName(t *testing.T) { + assert.Equal(t, instance.HostnameFromInstName("default"), "lima-default") + assert.Equal(t, instance.HostnameFromInstName("ubuntu-24.04"), "lima-ubuntu-24-04") + assert.Equal(t, instance.HostnameFromInstName("foo_bar.baz"), "lima-foo-bar-baz") +} diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index 1736b96fe33..047e1d1fba2 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -24,13 +24,8 @@ import ( "github.com/coreos/go-semver/semver" "github.com/docker/go-units" "github.com/goccy/go-yaml" + instance "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/ioutilx" - "github.com/lima-vm/lima/pkg/version" - "github.com/pbnjay/memory" - "github.com/sirupsen/logrus" - "golang.org/x/sys/cpu" - - "github.com/lima-vm/lima/pkg/identifierutil" "github.com/lima-vm/lima/pkg/localpathutil" . "github.com/lima-vm/lima/pkg/must" "github.com/lima-vm/lima/pkg/networks" @@ -38,7 +33,11 @@ import ( "github.com/lima-vm/lima/pkg/ptr" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" + "github.com/lima-vm/lima/pkg/version" "github.com/lima-vm/lima/pkg/version/versionutil" + "github.com/pbnjay/memory" + "github.com/sirupsen/logrus" + "golang.org/x/sys/cpu" ) const ( @@ -944,7 +943,7 @@ func executeGuestTemplate(format, instDir string, user User, param map[string]st name := filepath.Base(instDir) data := map[string]any{ "Name": name, - "Hostname": identifierutil.HostnameFromInstName(name), // TODO: support customization + "Hostname": instance.HostnameFromInstName(name), // TODO: support customization "UID": *user.UID, "User": *user.Name, "Home": *user.Home, diff --git a/pkg/sshutil/format.go b/pkg/sshutil/format.go index c2efda18aee..652b542931b 100644 --- a/pkg/sshutil/format.go +++ b/pkg/sshutil/format.go @@ -8,7 +8,7 @@ import ( "io" "strings" - "github.com/lima-vm/lima/pkg/identifierutil" + instance "github.com/lima-vm/lima/pkg/instance/hostname" ) // FormatT specifies the format type. @@ -42,11 +42,11 @@ const ( // Port 60022 FormatConfig = FormatT("config") -// TODO: consider supporting "url" format (ssh://USER@HOSTNAME:PORT) -// -// TODO: consider supporting "json" format -// It is unclear whether we can just map ssh "config" into JSON, as "config" has duplicated keys. -// (JSON supports duplicated keys too, but not all JSON implementations expect JSON with duplicated keys) + // TODO: consider supporting "url" format (ssh://USER@HOSTNAME:PORT). + // + // TODO: consider supporting "json" format. + // It is unclear whether we can just map ssh "config" into JSON, as "config" has duplicated keys. + // (JSON supports duplicated keys too, but not all JSON implementations expect JSON with duplicated keys). ) // Formats is the list of the supported formats. @@ -62,7 +62,7 @@ func quoteOption(o string) string { // Format formats the ssh options. func Format(w io.Writer, sshPath, instName string, format FormatT, opts []string) error { - fakeHostname := identifierutil.HostnameFromInstName(instName) // TODO: support customization + fakeHostname := instance.HostnameFromInstName(instName) // TODO: support customization switch format { case FormatCmd: args := []string{sshPath} diff --git a/pkg/store/instance.go b/pkg/store/instance.go index 2af8f814f93..c8f71b8a538 100644 --- a/pkg/store/instance.go +++ b/pkg/store/instance.go @@ -20,7 +20,7 @@ import ( "github.com/docker/go-units" hostagentclient "github.com/lima-vm/lima/pkg/hostagent/api/client" - "github.com/lima-vm/lima/pkg/identifierutil" + instancehostname "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" @@ -73,7 +73,7 @@ func Inspect(instName string) (*Instance, error) { inst := &Instance{ Name: instName, // TODO: support customizing hostname - Hostname: identifierutil.HostnameFromInstName(instName), + Hostname: instancehostname.HostnameFromInstName(instName), Status: StatusUnknown, } // InstanceDir validates the instName but does not check whether the instance exists From e6216df5b329aad433c198a45872435f04c657b0 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Mon, 26 May 2025 14:42:22 +0300 Subject: [PATCH 07/91] Split imports into sections: std, default, local Signed-off-by: Oleksandr Redko --- .golangci.yml | 8 +++++++- cmd/lima-guestagent/daemon_linux.go | 7 ++++--- cmd/lima-guestagent/install_systemd_linux.go | 3 ++- cmd/lima-guestagent/main_linux.go | 5 +++-- cmd/limactl/completion.go | 3 ++- cmd/limactl/copy.go | 5 +++-- cmd/limactl/debug.go | 3 ++- cmd/limactl/delete.go | 5 +++-- cmd/limactl/disk.go | 5 +++-- cmd/limactl/edit.go | 5 +++-- cmd/limactl/factory-reset.go | 5 +++-- cmd/limactl/genschema.go | 6 +++--- cmd/limactl/hostagent.go | 5 +++-- cmd/limactl/info.go | 3 ++- cmd/limactl/list.go | 3 ++- cmd/limactl/main.go | 7 ++++--- cmd/limactl/protect.go | 3 ++- cmd/limactl/prune.go | 5 +++-- cmd/limactl/restart.go | 3 ++- cmd/limactl/shell.go | 9 +++++---- cmd/limactl/show-ssh.go | 5 +++-- cmd/limactl/snapshot.go | 6 +++--- cmd/limactl/start-at-login_unix.go | 5 +++-- cmd/limactl/start.go | 5 +++-- cmd/limactl/stop.go | 3 ++- cmd/limactl/sudoers.go | 3 ++- cmd/limactl/sudoers_darwin.go | 3 ++- cmd/limactl/template.go | 5 +++-- cmd/limactl/tunnel.go | 5 +++-- cmd/limactl/unprotect.go | 3 ++- cmd/limactl/usernet.go | 3 ++- pkg/cidata/cidata.go | 3 ++- pkg/cidata/cidata_test.go | 4 ++-- pkg/cidata/template.go | 4 ++-- pkg/downloader/downloader.go | 5 +++-- pkg/editutil/editutil.go | 3 ++- pkg/fileutils/download.go | 3 ++- pkg/guestagent/api/client/client.go | 3 ++- pkg/guestagent/api/server/server.go | 5 +++-- pkg/guestagent/guestagent_linux.go | 5 +++-- pkg/hostagent/events/watcher.go | 3 ++- pkg/hostagent/hostagent.go | 6 +++--- pkg/hostagent/inotify.go | 3 ++- pkg/hostagent/mount.go | 5 +++-- pkg/hostagent/port.go | 5 +++-- pkg/hostagent/port_darwin.go | 5 +++-- pkg/hostagent/requirements.go | 3 ++- pkg/instance/ansible.go | 3 ++- pkg/instance/hostname/hostname_test.go | 3 ++- pkg/instance/restart.go | 3 ++- pkg/instance/start.go | 16 ++++++++-------- pkg/instance/stop.go | 3 ++- pkg/limainfo/limainfo.go | 3 ++- pkg/limatmpl/embed.go | 3 ++- pkg/limatmpl/embed_test.go | 3 ++- pkg/limatmpl/locator.go | 3 ++- pkg/limatmpl/locator_test.go | 3 ++- pkg/limayaml/defaults.go | 7 ++++--- pkg/limayaml/defaults_test.go | 5 +++-- pkg/limayaml/load.go | 3 ++- pkg/limayaml/marshal.go | 3 ++- pkg/limayaml/marshal_test.go | 3 ++- pkg/limayaml/validate.go | 3 ++- pkg/nativeimgutil/nativeimgutil.go | 3 ++- pkg/networks/commands_test.go | 3 ++- pkg/networks/config.go | 3 ++- pkg/networks/reconcile/reconcile.go | 3 ++- pkg/networks/usernet/client.go | 1 + pkg/networks/usernet/config.go | 1 + pkg/networks/usernet/config_test.go | 3 ++- pkg/networks/usernet/recoincile.go | 3 ++- pkg/osutil/user.go | 3 ++- pkg/portfwd/client.go | 3 ++- pkg/portfwd/forward.go | 3 ++- pkg/portfwd/listener.go | 3 ++- pkg/qemu/entitlementutil/entitlementutil.go | 4 ++-- pkg/qemu/qemu.go | 10 +++++----- pkg/qemu/qemu_driver.go | 3 ++- pkg/sshutil/sshutil.go | 7 ++++--- pkg/store/disk.go | 1 + pkg/store/fuzz_test.go | 3 ++- pkg/store/instance.go | 3 ++- pkg/store/instance_test.go | 3 ++- pkg/store/store.go | 1 + pkg/usrlocalsharelima/usrlocalsharelima.go | 3 ++- pkg/vz/disk.go | 1 + pkg/vz/network_darwin.go | 1 - pkg/vz/rosetta_directory_share_arm64.go | 3 ++- pkg/vz/vm_darwin.go | 3 ++- pkg/vz/vz_driver_darwin.go | 1 - pkg/wsl2/fs.go | 3 ++- pkg/wsl2/vm_windows.go | 3 ++- pkg/wsl2/wsl_driver_windows.go | 3 ++- 93 files changed, 225 insertions(+), 139 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 9fe5e6782fd..3634b2b9170 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -146,6 +146,12 @@ issues: max-same-issues: 0 formatters: enable: + - gci - gofmt - gofumpt - - goimports + settings: + gci: + sections: + - standard + - default + - localmodule diff --git a/cmd/lima-guestagent/daemon_linux.go b/cmd/lima-guestagent/daemon_linux.go index f15f5ae8d21..919edb19a53 100644 --- a/cmd/lima-guestagent/daemon_linux.go +++ b/cmd/lima-guestagent/daemon_linux.go @@ -9,13 +9,14 @@ import ( "os" "time" + "github.com/mdlayher/vsock" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/guestagent" "github.com/lima-vm/lima/pkg/guestagent/api/server" "github.com/lima-vm/lima/pkg/guestagent/serialport" "github.com/lima-vm/lima/pkg/portfwdserver" - "github.com/mdlayher/vsock" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newDaemonCommand() *cobra.Command { diff --git a/cmd/lima-guestagent/install_systemd_linux.go b/cmd/lima-guestagent/install_systemd_linux.go index d385df94ea1..bc654a56246 100644 --- a/cmd/lima-guestagent/install_systemd_linux.go +++ b/cmd/lima-guestagent/install_systemd_linux.go @@ -12,9 +12,10 @@ import ( "path/filepath" "strings" - "github.com/lima-vm/lima/pkg/textutil" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/textutil" ) func newInstallSystemdCommand() *cobra.Command { diff --git a/cmd/lima-guestagent/main_linux.go b/cmd/lima-guestagent/main_linux.go index dbaaf3014ea..ac73b743ea4 100644 --- a/cmd/lima-guestagent/main_linux.go +++ b/cmd/lima-guestagent/main_linux.go @@ -6,10 +6,11 @@ package main import ( "strings" - "github.com/lima-vm/lima/pkg/debugutil" - "github.com/lima-vm/lima/pkg/version" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/debugutil" + "github.com/lima-vm/lima/pkg/version" ) func main() { diff --git a/cmd/limactl/completion.go b/cmd/limactl/completion.go index 5a01ed52b38..f69005b9d31 100644 --- a/cmd/limactl/completion.go +++ b/cmd/limactl/completion.go @@ -6,9 +6,10 @@ package main import ( "strings" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/templatestore" - "github.com/spf13/cobra" ) func bashCompleteInstanceNames(_ *cobra.Command) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/limactl/copy.go b/cmd/limactl/copy.go index 8b4a138f9f5..1f422d210a6 100644 --- a/cmd/limactl/copy.go +++ b/cmd/limactl/copy.go @@ -13,11 +13,12 @@ import ( "strings" "github.com/coreos/go-semver/semver" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) const copyHelp = `Copy files between host and guest diff --git a/cmd/limactl/debug.go b/cmd/limactl/debug.go index 124bcf4c396..21bfef65987 100644 --- a/cmd/limactl/debug.go +++ b/cmd/limactl/debug.go @@ -7,9 +7,10 @@ import ( "strconv" "time" - "github.com/lima-vm/lima/pkg/hostagent/dns" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/hostagent/dns" ) func newDebugCommand() *cobra.Command { diff --git a/cmd/limactl/delete.go b/cmd/limactl/delete.go index 827555c9044..407803566d4 100644 --- a/cmd/limactl/delete.go +++ b/cmd/limactl/delete.go @@ -9,12 +9,13 @@ import ( "os" "runtime" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/autostart" "github.com/lima-vm/lima/pkg/instance" networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/store" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newDeleteCommand() *cobra.Command { diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go index db9788c413c..cf95c0162cf 100644 --- a/cmd/limactl/disk.go +++ b/cmd/limactl/disk.go @@ -15,12 +15,13 @@ import ( contfs "github.com/containerd/continuity/fs" "github.com/docker/go-units" "github.com/lima-vm/go-qcow2reader" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newDiskCommand() *cobra.Command { diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index a411df71863..33ee9581333 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -10,6 +10,9 @@ import ( "os" "path/filepath" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/cmd/limactl/editflags" "github.com/lima-vm/lima/pkg/editutil" "github.com/lima-vm/lima/pkg/instance" @@ -19,8 +22,6 @@ import ( "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/uiutil" "github.com/lima-vm/lima/pkg/yqutil" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newEditCommand() *cobra.Command { diff --git a/cmd/limactl/factory-reset.go b/cmd/limactl/factory-reset.go index a5303098cb6..9d0add8f75b 100644 --- a/cmd/limactl/factory-reset.go +++ b/cmd/limactl/factory-reset.go @@ -9,12 +9,13 @@ import ( "path/filepath" "strings" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/cidata" "github.com/lima-vm/lima/pkg/instance" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newFactoryResetCommand() *cobra.Command { diff --git a/cmd/limactl/genschema.go b/cmd/limactl/genschema.go index d1be43aeb92..bb7f2236600 100644 --- a/cmd/limactl/genschema.go +++ b/cmd/limactl/genschema.go @@ -10,12 +10,12 @@ import ( "os" "github.com/invopop/jsonschema" - "github.com/lima-vm/lima/pkg/jsonschemautil" - "github.com/lima-vm/lima/pkg/limayaml" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" orderedmap "github.com/wk8/go-ordered-map/v2" - "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/jsonschemautil" + "github.com/lima-vm/lima/pkg/limayaml" ) func newGenSchemaCommand() *cobra.Command { diff --git a/cmd/limactl/hostagent.go b/cmd/limactl/hostagent.go index ae02207d368..d850655500c 100644 --- a/cmd/limactl/hostagent.go +++ b/cmd/limactl/hostagent.go @@ -15,10 +15,11 @@ import ( "strconv" "syscall" - "github.com/lima-vm/lima/pkg/hostagent" - "github.com/lima-vm/lima/pkg/hostagent/api/server" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/hostagent" + "github.com/lima-vm/lima/pkg/hostagent/api/server" ) func newHostagentCommand() *cobra.Command { diff --git a/cmd/limactl/info.go b/cmd/limactl/info.go index 73ef5c1b8d4..8ece9ab86ea 100644 --- a/cmd/limactl/info.go +++ b/cmd/limactl/info.go @@ -7,8 +7,9 @@ import ( "encoding/json" "fmt" - "github.com/lima-vm/lima/pkg/limainfo" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/limainfo" ) func newInfoCommand() *cobra.Command { diff --git a/cmd/limactl/list.go b/cmd/limactl/list.go index b55d96f70d0..20706ad9874 100644 --- a/cmd/limactl/list.go +++ b/cmd/limactl/list.go @@ -12,10 +12,11 @@ import ( "strings" "github.com/cheggaaa/pb/v3/termutil" - "github.com/lima-vm/lima/pkg/store" "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/store" ) func fieldNames() []string { diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go index d56047f512a..a006f0bb33f 100644 --- a/cmd/limactl/main.go +++ b/cmd/limactl/main.go @@ -11,14 +11,15 @@ import ( "runtime" "strings" + "github.com/mattn/go-isatty" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/debugutil" "github.com/lima-vm/lima/pkg/fsutil" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/version" - "github.com/mattn/go-isatty" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) const ( diff --git a/cmd/limactl/protect.go b/cmd/limactl/protect.go index 3614060981e..47e3e0a95c2 100644 --- a/cmd/limactl/protect.go +++ b/cmd/limactl/protect.go @@ -7,9 +7,10 @@ import ( "errors" "fmt" - "github.com/lima-vm/lima/pkg/store" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/store" ) func newProtectCommand() *cobra.Command { diff --git a/cmd/limactl/prune.go b/cmd/limactl/prune.go index b52f503ddbf..76266aa07a2 100644 --- a/cmd/limactl/prune.go +++ b/cmd/limactl/prune.go @@ -7,12 +7,13 @@ import ( "maps" "os" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/downloader" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/templatestore" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newPruneCommand() *cobra.Command { diff --git a/cmd/limactl/restart.go b/cmd/limactl/restart.go index 39fec1a1570..f281aff9da1 100644 --- a/cmd/limactl/restart.go +++ b/cmd/limactl/restart.go @@ -4,9 +4,10 @@ package main import ( + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/instance" "github.com/lima-vm/lima/pkg/store" - "github.com/spf13/cobra" ) func newRestartCommand() *cobra.Command { diff --git a/cmd/limactl/shell.go b/cmd/limactl/shell.go index 3aba88208e3..2c7498d6acc 100644 --- a/cmd/limactl/shell.go +++ b/cmd/limactl/shell.go @@ -14,16 +14,17 @@ import ( "al.essio.dev/pkg/shellescape" "github.com/coreos/go-semver/semver" + "github.com/lima-vm/sshocker/pkg/ssh" + "github.com/mattn/go-isatty" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/instance" "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/limayaml" networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store" - "github.com/lima-vm/sshocker/pkg/ssh" - "github.com/mattn/go-isatty" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) const shellHelp = `Execute shell in Lima diff --git a/cmd/limactl/show-ssh.go b/cmd/limactl/show-ssh.go index b5103756c74..eb2a1de4b03 100644 --- a/cmd/limactl/show-ssh.go +++ b/cmd/limactl/show-ssh.go @@ -10,12 +10,13 @@ import ( "path/filepath" "strings" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) const showSSHExample = ` diff --git a/cmd/limactl/snapshot.go b/cmd/limactl/snapshot.go index 30117c07362..6f7e198cb53 100644 --- a/cmd/limactl/snapshot.go +++ b/cmd/limactl/snapshot.go @@ -8,11 +8,11 @@ import ( "fmt" "strings" - "github.com/lima-vm/lima/pkg/snapshot" - "github.com/lima-vm/lima/pkg/store" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/snapshot" + "github.com/lima-vm/lima/pkg/store" ) func newSnapshotCommand() *cobra.Command { diff --git a/cmd/limactl/start-at-login_unix.go b/cmd/limactl/start-at-login_unix.go index 6b4b189d015..e127caa368f 100644 --- a/cmd/limactl/start-at-login_unix.go +++ b/cmd/limactl/start-at-login_unix.go @@ -10,10 +10,11 @@ import ( "os" "runtime" - "github.com/lima-vm/lima/pkg/autostart" - "github.com/lima-vm/lima/pkg/store" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/autostart" + "github.com/lima-vm/lima/pkg/store" ) func startAtLoginAction(cmd *cobra.Command, args []string) error { diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index 5187de6005e..ad0534847bc 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -11,6 +11,9 @@ import ( "runtime" "strings" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/cmd/limactl/editflags" "github.com/lima-vm/lima/pkg/editutil" "github.com/lima-vm/lima/pkg/instance" @@ -22,8 +25,6 @@ import ( "github.com/lima-vm/lima/pkg/templatestore" "github.com/lima-vm/lima/pkg/uiutil" "github.com/lima-vm/lima/pkg/yqutil" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func registerCreateFlags(cmd *cobra.Command, commentPrefix string) { diff --git a/cmd/limactl/stop.go b/cmd/limactl/stop.go index 5a8c91cae16..3f1ee6d2504 100644 --- a/cmd/limactl/stop.go +++ b/cmd/limactl/stop.go @@ -4,10 +4,11 @@ package main import ( + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/instance" networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/store" - "github.com/spf13/cobra" ) func newStopCommand() *cobra.Command { diff --git a/cmd/limactl/sudoers.go b/cmd/limactl/sudoers.go index 7478312edb3..49e28cc2d99 100644 --- a/cmd/limactl/sudoers.go +++ b/cmd/limactl/sudoers.go @@ -6,8 +6,9 @@ package main import ( "fmt" - "github.com/lima-vm/lima/pkg/networks" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/networks" ) const socketVMNetURL = "https://lima-vm.io/docs/config/network/vmnet/#socket_vmnet" diff --git a/cmd/limactl/sudoers_darwin.go b/cmd/limactl/sudoers_darwin.go index 96fb265bb83..af26cb7aba0 100644 --- a/cmd/limactl/sudoers_darwin.go +++ b/cmd/limactl/sudoers_darwin.go @@ -8,9 +8,10 @@ import ( "fmt" "io" - "github.com/lima-vm/lima/pkg/networks" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/networks" ) func sudoersAction(cmd *cobra.Command, args []string) error { diff --git a/cmd/limactl/template.go b/cmd/limactl/template.go index 73d22b0b37f..fbbb945ccb0 100644 --- a/cmd/limactl/template.go +++ b/cmd/limactl/template.go @@ -9,12 +9,13 @@ import ( "os" "path/filepath" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/limatmpl" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/yqutil" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func newTemplateCommand() *cobra.Command { diff --git a/cmd/limactl/tunnel.go b/cmd/limactl/tunnel.go index 9e69028186b..451376175be 100644 --- a/cmd/limactl/tunnel.go +++ b/cmd/limactl/tunnel.go @@ -11,11 +11,12 @@ import ( "runtime" "strconv" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/freeport" "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) const tunnelHelp = `Create a tunnel for Lima diff --git a/cmd/limactl/unprotect.go b/cmd/limactl/unprotect.go index 5cb9b303df8..72629678e36 100644 --- a/cmd/limactl/unprotect.go +++ b/cmd/limactl/unprotect.go @@ -7,9 +7,10 @@ import ( "errors" "fmt" - "github.com/lima-vm/lima/pkg/store" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/store" ) func newUnprotectCommand() *cobra.Command { diff --git a/cmd/limactl/usernet.go b/cmd/limactl/usernet.go index 66cf06cf39c..8a6854a76bb 100644 --- a/cmd/limactl/usernet.go +++ b/cmd/limactl/usernet.go @@ -9,8 +9,9 @@ import ( "os" "strconv" - "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/networks/usernet" ) func newUsernetCommand() *cobra.Command { diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index 0b52e26f7ca..dba3cbc9368 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -20,6 +20,8 @@ import ( "time" "github.com/docker/go-units" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/debugutil" instance "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/iso9660util" @@ -30,7 +32,6 @@ import ( "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) var netLookupIP = func(host string) []net.IP { diff --git a/pkg/cidata/cidata_test.go b/pkg/cidata/cidata_test.go index 1e3fafc549f..499456529e2 100644 --- a/pkg/cidata/cidata_test.go +++ b/pkg/cidata/cidata_test.go @@ -9,9 +9,9 @@ import ( "strings" "testing" - "github.com/lima-vm/lima/pkg/networks" - "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/networks" ) func fakeLookupIP(_ string) []net.IP { diff --git a/pkg/cidata/template.go b/pkg/cidata/template.go index 70cba120318..2874c1c4f8a 100644 --- a/pkg/cidata/template.go +++ b/pkg/cidata/template.go @@ -11,9 +11,9 @@ import ( "io/fs" "path" - "github.com/lima-vm/lima/pkg/iso9660util" - "github.com/containerd/containerd/identifiers" + + "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/textutil" ) diff --git a/pkg/downloader/downloader.go b/pkg/downloader/downloader.go index 785261ea385..ce136d653c1 100644 --- a/pkg/downloader/downloader.go +++ b/pkg/downloader/downloader.go @@ -21,12 +21,13 @@ import ( "github.com/cheggaaa/pb/v3" "github.com/containerd/continuity/fs" + "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/httpclientutil" "github.com/lima-vm/lima/pkg/localpathutil" "github.com/lima-vm/lima/pkg/lockutil" "github.com/lima-vm/lima/pkg/progressbar" - "github.com/opencontainers/go-digest" - "github.com/sirupsen/logrus" ) // HideProgress is used only for testing. diff --git a/pkg/editutil/editutil.go b/pkg/editutil/editutil.go index a8a172cd879..ffef4dfe027 100644 --- a/pkg/editutil/editutil.go +++ b/pkg/editutil/editutil.go @@ -11,10 +11,11 @@ import ( "path/filepath" "strings" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/editutil/editorcmd" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) func fileWarning(filename string) string { diff --git a/pkg/fileutils/download.go b/pkg/fileutils/download.go index bb54604e324..a2f4077759f 100644 --- a/pkg/fileutils/download.go +++ b/pkg/fileutils/download.go @@ -9,9 +9,10 @@ import ( "fmt" "path" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/downloader" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/sirupsen/logrus" ) // ErrSkipped is returned when the downloader did not attempt to download the specified file. diff --git a/pkg/guestagent/api/client/client.go b/pkg/guestagent/api/client/client.go index 934d15341de..544123a5fb9 100644 --- a/pkg/guestagent/api/client/client.go +++ b/pkg/guestagent/api/client/client.go @@ -8,10 +8,11 @@ import ( "math" "net" - "github.com/lima-vm/lima/pkg/guestagent/api" "google.golang.org/grpc" "google.golang.org/grpc/resolver" "google.golang.org/protobuf/types/known/emptypb" + + "github.com/lima-vm/lima/pkg/guestagent/api" ) type GuestAgentClient struct { diff --git a/pkg/guestagent/api/server/server.go b/pkg/guestagent/api/server/server.go index 84018613a0b..b8f6e249dde 100644 --- a/pkg/guestagent/api/server/server.go +++ b/pkg/guestagent/api/server/server.go @@ -7,11 +7,12 @@ import ( "context" "net" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/lima-vm/lima/pkg/guestagent" "github.com/lima-vm/lima/pkg/guestagent/api" "github.com/lima-vm/lima/pkg/portfwdserver" - "google.golang.org/grpc" - "google.golang.org/protobuf/types/known/emptypb" ) func StartServer(lis net.Listener, guest *GuestServer) error { diff --git a/pkg/guestagent/guestagent_linux.go b/pkg/guestagent/guestagent_linux.go index f64a672e123..2013c61158a 100644 --- a/pkg/guestagent/guestagent_linux.go +++ b/pkg/guestagent/guestagent_linux.go @@ -14,13 +14,14 @@ import ( "github.com/elastic/go-libaudit/v2" "github.com/elastic/go-libaudit/v2/auparse" + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/types/known/timestamppb" + "github.com/lima-vm/lima/pkg/guestagent/api" "github.com/lima-vm/lima/pkg/guestagent/iptables" "github.com/lima-vm/lima/pkg/guestagent/kubernetesservice" "github.com/lima-vm/lima/pkg/guestagent/procnettcp" "github.com/lima-vm/lima/pkg/guestagent/timesync" - "github.com/sirupsen/logrus" - "google.golang.org/protobuf/types/known/timestamppb" ) func New(newTicker func() (<-chan time.Time, func()), iptablesIdle time.Duration) (Agent, error) { diff --git a/pkg/hostagent/events/watcher.go b/pkg/hostagent/events/watcher.go index 8d1fca47f22..303afe2b081 100644 --- a/pkg/hostagent/events/watcher.go +++ b/pkg/hostagent/events/watcher.go @@ -9,9 +9,10 @@ import ( "fmt" "time" - "github.com/lima-vm/lima/pkg/logrusutil" "github.com/nxadm/tail" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/logrusutil" ) func Watch(ctx context.Context, haStdoutPath, haStderrPath string, begin time.Time, onEvent func(Event) bool) error { diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index 921caa122e5..ee8b42bddb8 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -19,6 +19,9 @@ import ( "sync" "time" + "github.com/lima-vm/sshocker/pkg/ssh" + "github.com/sethvargo/go-password/password" + "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -39,9 +42,6 @@ import ( "github.com/lima-vm/lima/pkg/sshutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/lima-vm/sshocker/pkg/ssh" - "github.com/sethvargo/go-password/password" - "github.com/sirupsen/logrus" ) type HostAgent struct { diff --git a/pkg/hostagent/inotify.go b/pkg/hostagent/inotify.go index 374e3408c8a..10bb2a4b3c0 100644 --- a/pkg/hostagent/inotify.go +++ b/pkg/hostagent/inotify.go @@ -10,10 +10,11 @@ import ( "path/filepath" "strings" - guestagentapi "github.com/lima-vm/lima/pkg/guestagent/api" "github.com/rjeczalik/notify" "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/timestamppb" + + guestagentapi "github.com/lima-vm/lima/pkg/guestagent/api" ) const CacheSize = 10000 diff --git a/pkg/hostagent/mount.go b/pkg/hostagent/mount.go index 877d759fd0f..5992a510fdd 100644 --- a/pkg/hostagent/mount.go +++ b/pkg/hostagent/mount.go @@ -9,10 +9,11 @@ import ( "os" "runtime" - "github.com/lima-vm/lima/pkg/ioutilx" - "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/sshocker/pkg/reversesshfs" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/ioutilx" + "github.com/lima-vm/lima/pkg/limayaml" ) type mount struct { diff --git a/pkg/hostagent/port.go b/pkg/hostagent/port.go index 040d7594092..805e9cbd76d 100644 --- a/pkg/hostagent/port.go +++ b/pkg/hostagent/port.go @@ -7,10 +7,11 @@ import ( "context" "net" - "github.com/lima-vm/lima/pkg/guestagent/api" - "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/sshocker/pkg/ssh" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/guestagent/api" + "github.com/lima-vm/lima/pkg/limayaml" ) type portForwarder struct { diff --git a/pkg/hostagent/port_darwin.go b/pkg/hostagent/port_darwin.go index 5ac63b41388..87bdb271f96 100644 --- a/pkg/hostagent/port_darwin.go +++ b/pkg/hostagent/port_darwin.go @@ -12,10 +12,11 @@ import ( "strconv" "strings" - "github.com/lima-vm/lima/pkg/bicopy" - "github.com/lima-vm/lima/pkg/portfwd" "github.com/lima-vm/sshocker/pkg/ssh" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/bicopy" + "github.com/lima-vm/lima/pkg/portfwd" ) // forwardTCP is not thread-safe. diff --git a/pkg/hostagent/requirements.go b/pkg/hostagent/requirements.go index 7d8f24f3549..f57e495e7c6 100644 --- a/pkg/hostagent/requirements.go +++ b/pkg/hostagent/requirements.go @@ -9,9 +9,10 @@ import ( "strings" "time" - "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/sshocker/pkg/ssh" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/limayaml" ) func (a *HostAgent) waitForRequirements(label string, requirements []requirement) error { diff --git a/pkg/instance/ansible.go b/pkg/instance/ansible.go index e2fa1c4f073..36af0f8c75e 100644 --- a/pkg/instance/ansible.go +++ b/pkg/instance/ansible.go @@ -11,10 +11,11 @@ import ( "path/filepath" "github.com/goccy/go-yaml" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) func runAnsibleProvision(ctx context.Context, inst *store.Instance) error { diff --git a/pkg/instance/hostname/hostname_test.go b/pkg/instance/hostname/hostname_test.go index 69620638566..8173d62d2a1 100644 --- a/pkg/instance/hostname/hostname_test.go +++ b/pkg/instance/hostname/hostname_test.go @@ -6,8 +6,9 @@ package instance_test import ( "testing" - instance "github.com/lima-vm/lima/pkg/instance/hostname" "gotest.tools/v3/assert" + + instance "github.com/lima-vm/lima/pkg/instance/hostname" ) func TestHostnameFromInstName(t *testing.T) { diff --git a/pkg/instance/restart.go b/pkg/instance/restart.go index 04656f04558..c63c2e40487 100644 --- a/pkg/instance/restart.go +++ b/pkg/instance/restart.go @@ -6,9 +6,10 @@ package instance import ( "context" + "github.com/sirupsen/logrus" + networks "github.com/lima-vm/lima/pkg/networks/reconcile" "github.com/lima-vm/lima/pkg/store" - "github.com/sirupsen/logrus" ) const launchHostAgentForeground = false diff --git a/pkg/instance/start.go b/pkg/instance/start.go index 9144d9affde..1df00c785a3 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -18,22 +18,22 @@ import ( "github.com/docker/go-units" "github.com/lima-vm/go-qcow2reader" - "github.com/lima-vm/lima/pkg/driver" - "github.com/lima-vm/lima/pkg/driverutil" - "github.com/lima-vm/lima/pkg/executil" - "github.com/lima-vm/lima/pkg/nativeimgutil" - "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" - "github.com/lima-vm/lima/pkg/usrlocalsharelima" "github.com/mattn/go-isatty" + "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/downloader" + "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/driverutil" + "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/fileutils" hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" "github.com/lima-vm/lima/pkg/limayaml" + "github.com/lima-vm/lima/pkg/nativeimgutil" + "github.com/lima-vm/lima/pkg/osutil" + "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/usrlocalsharelima" ) // DefaultWatchHostAgentEventsTimeout is the duration to wait for the instance diff --git a/pkg/instance/stop.go b/pkg/instance/stop.go index 12b624ed62e..60b8d9b3fe9 100644 --- a/pkg/instance/stop.go +++ b/pkg/instance/stop.go @@ -12,11 +12,12 @@ import ( "strings" "time" + "github.com/sirupsen/logrus" + hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) func StopGracefully(ctx context.Context, inst *store.Instance, isRestart bool) error { diff --git a/pkg/limainfo/limainfo.go b/pkg/limainfo/limainfo.go index 89a646ecae5..5311cca2805 100644 --- a/pkg/limainfo/limainfo.go +++ b/pkg/limainfo/limainfo.go @@ -7,13 +7,14 @@ import ( "errors" "io/fs" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/driverutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/templatestore" "github.com/lima-vm/lima/pkg/usrlocalsharelima" "github.com/lima-vm/lima/pkg/version" - "github.com/sirupsen/logrus" ) type LimaInfo struct { diff --git a/pkg/limatmpl/embed.go b/pkg/limatmpl/embed.go index 202174f56e8..1a0d9d8915d 100644 --- a/pkg/limatmpl/embed.go +++ b/pkg/limatmpl/embed.go @@ -16,12 +16,13 @@ import ( "unicode" "github.com/coreos/go-semver/semver" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/version/versionutil" "github.com/lima-vm/lima/pkg/yqutil" - "github.com/sirupsen/logrus" ) // Embed will recursively resolve all "base" dependencies and update the diff --git a/pkg/limatmpl/embed_test.go b/pkg/limatmpl/embed_test.go index e1db9ba213a..4fa2db58b60 100644 --- a/pkg/limatmpl/embed_test.go +++ b/pkg/limatmpl/embed_test.go @@ -11,10 +11,11 @@ import ( "strings" "testing" - "github.com/lima-vm/lima/pkg/limayaml" "github.com/sirupsen/logrus" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" + + "github.com/lima-vm/lima/pkg/limayaml" ) type embedTestCase struct { diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go index 218311af100..6f952b2007a 100644 --- a/pkg/limatmpl/locator.go +++ b/pkg/limatmpl/locator.go @@ -17,10 +17,11 @@ import ( "strings" "github.com/containerd/containerd/identifiers" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/templatestore" - "github.com/sirupsen/logrus" ) const yBytesLimit = 4 * 1024 * 1024 // 4MiB diff --git a/pkg/limatmpl/locator_test.go b/pkg/limatmpl/locator_test.go index 6c2c03d838a..7f6b16dc4bb 100644 --- a/pkg/limatmpl/locator_test.go +++ b/pkg/limatmpl/locator_test.go @@ -8,9 +8,10 @@ import ( "runtime" "testing" + "gotest.tools/v3/assert" + "github.com/lima-vm/lima/pkg/limatmpl" "github.com/lima-vm/lima/pkg/limayaml" - "gotest.tools/v3/assert" ) func TestInstNameFromImageURL(t *testing.T) { diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index 047e1d1fba2..2351287a60e 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -24,6 +24,10 @@ import ( "github.com/coreos/go-semver/semver" "github.com/docker/go-units" "github.com/goccy/go-yaml" + "github.com/pbnjay/memory" + "github.com/sirupsen/logrus" + "golang.org/x/sys/cpu" + instance "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/localpathutil" @@ -35,9 +39,6 @@ import ( "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/version" "github.com/lima-vm/lima/pkg/version/versionutil" - "github.com/pbnjay/memory" - "github.com/sirupsen/logrus" - "golang.org/x/sys/cpu" ) const ( diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go index 6384270927d..70f6810c855 100644 --- a/pkg/limayaml/defaults_test.go +++ b/pkg/limayaml/defaults_test.go @@ -16,13 +16,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sirupsen/logrus" + "gotest.tools/v3/assert" + "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/ptr" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" - "gotest.tools/v3/assert" ) func TestFillDefault(t *testing.T) { diff --git a/pkg/limayaml/load.go b/pkg/limayaml/load.go index 87cb4890aa9..13f71e8d92e 100644 --- a/pkg/limayaml/load.go +++ b/pkg/limayaml/load.go @@ -9,9 +9,10 @@ import ( "os" "path/filepath" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) // Load loads the yaml and fulfills unspecified fields with the default values. diff --git a/pkg/limayaml/marshal.go b/pkg/limayaml/marshal.go index 70caee2fced..22c42f02373 100644 --- a/pkg/limayaml/marshal.go +++ b/pkg/limayaml/marshal.go @@ -7,8 +7,9 @@ import ( "fmt" "github.com/goccy/go-yaml" - "github.com/lima-vm/lima/pkg/yqutil" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/yqutil" ) const ( diff --git a/pkg/limayaml/marshal_test.go b/pkg/limayaml/marshal_test.go index 0f77b11f401..a8682bfa9cb 100644 --- a/pkg/limayaml/marshal_test.go +++ b/pkg/limayaml/marshal_test.go @@ -6,8 +6,9 @@ package limayaml import ( "testing" - "github.com/lima-vm/lima/pkg/ptr" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/ptr" ) func TestMarshalEmpty(t *testing.T) { diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 42c4faa5890..eeef301c90a 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -20,12 +20,13 @@ import ( "github.com/containerd/containerd/identifiers" "github.com/coreos/go-semver/semver" "github.com/docker/go-units" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/localpathutil" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/version" "github.com/lima-vm/lima/pkg/version/versionutil" - "github.com/sirupsen/logrus" ) func validateFileObject(f File, fieldName string) error { diff --git a/pkg/nativeimgutil/nativeimgutil.go b/pkg/nativeimgutil/nativeimgutil.go index 52dcf2eb9c1..094a5571168 100644 --- a/pkg/nativeimgutil/nativeimgutil.go +++ b/pkg/nativeimgutil/nativeimgutil.go @@ -18,8 +18,9 @@ import ( "github.com/lima-vm/go-qcow2reader/convert" "github.com/lima-vm/go-qcow2reader/image/qcow2" "github.com/lima-vm/go-qcow2reader/image/raw" - "github.com/lima-vm/lima/pkg/progressbar" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/progressbar" ) // Disk image size must be aigned to sector size. Qemu block layer is rounding diff --git a/pkg/networks/commands_test.go b/pkg/networks/commands_test.go index b3b958b5a0a..7da2bae627c 100644 --- a/pkg/networks/commands_test.go +++ b/pkg/networks/commands_test.go @@ -8,8 +8,9 @@ import ( "runtime" "testing" - "github.com/lima-vm/lima/pkg/store/dirnames" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/store/dirnames" ) func TestCheck(t *testing.T) { diff --git a/pkg/networks/config.go b/pkg/networks/config.go index 984a7a51d27..033231c9dc7 100644 --- a/pkg/networks/config.go +++ b/pkg/networks/config.go @@ -13,10 +13,11 @@ import ( "sync" "github.com/goccy/go-yaml" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/textutil" - "github.com/sirupsen/logrus" ) //go:embed networks.TEMPLATE.yaml diff --git a/pkg/networks/reconcile/reconcile.go b/pkg/networks/reconcile/reconcile.go index 35c269efdfc..f0f383a0128 100644 --- a/pkg/networks/reconcile/reconcile.go +++ b/pkg/networks/reconcile/reconcile.go @@ -14,12 +14,13 @@ import ( "sync" "time" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/dirnames" - "github.com/sirupsen/logrus" ) func Reconcile(ctx context.Context, newInst string) error { diff --git a/pkg/networks/usernet/client.go b/pkg/networks/usernet/client.go index 014b9397586..2296f103ed1 100644 --- a/pkg/networks/usernet/client.go +++ b/pkg/networks/usernet/client.go @@ -16,6 +16,7 @@ import ( gvproxyclient "github.com/containers/gvisor-tap-vsock/pkg/client" "github.com/containers/gvisor-tap-vsock/pkg/types" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/httpclientutil" "github.com/lima-vm/lima/pkg/limayaml" diff --git a/pkg/networks/usernet/config.go b/pkg/networks/usernet/config.go index 76940d60001..fe97232db16 100644 --- a/pkg/networks/usernet/config.go +++ b/pkg/networks/usernet/config.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/apparentlymart/go-cidr/cidr" + "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store/dirnames" diff --git a/pkg/networks/usernet/config_test.go b/pkg/networks/usernet/config_test.go index 832085a42a0..dc92d59fd5c 100644 --- a/pkg/networks/usernet/config_test.go +++ b/pkg/networks/usernet/config_test.go @@ -7,8 +7,9 @@ import ( "net" "testing" - "github.com/lima-vm/lima/pkg/networks" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/networks" ) func TestUsernetConfig(t *testing.T) { diff --git a/pkg/networks/usernet/recoincile.go b/pkg/networks/usernet/recoincile.go index 7304e7241f7..a85bc81c8c7 100644 --- a/pkg/networks/usernet/recoincile.go +++ b/pkg/networks/usernet/recoincile.go @@ -16,11 +16,12 @@ import ( "strings" "time" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/lockutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/dirnames" - "github.com/sirupsen/logrus" ) // Start starts a instance a usernet network with the given name. diff --git a/pkg/osutil/user.go b/pkg/osutil/user.go index 8b09091f069..1ff1c02a1e8 100644 --- a/pkg/osutil/user.go +++ b/pkg/osutil/user.go @@ -13,9 +13,10 @@ import ( "strings" "sync" + "github.com/sirupsen/logrus" + . "github.com/lima-vm/lima/pkg/must" "github.com/lima-vm/lima/pkg/version/versionutil" - "github.com/sirupsen/logrus" ) type User struct { diff --git a/pkg/portfwd/client.go b/pkg/portfwd/client.go index 1c24c0690ec..d76bfe9e716 100644 --- a/pkg/portfwd/client.go +++ b/pkg/portfwd/client.go @@ -10,10 +10,11 @@ import ( "time" "github.com/containers/gvisor-tap-vsock/pkg/services/forwarder" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/bicopy" "github.com/lima-vm/lima/pkg/guestagent/api" guestagentclient "github.com/lima-vm/lima/pkg/guestagent/api/client" - "github.com/sirupsen/logrus" ) func HandleTCPConnection(ctx context.Context, client *guestagentclient.GuestAgentClient, conn net.Conn, guestAddr string) { diff --git a/pkg/portfwd/forward.go b/pkg/portfwd/forward.go index 846fb98bb11..7d6c001c5c4 100644 --- a/pkg/portfwd/forward.go +++ b/pkg/portfwd/forward.go @@ -8,10 +8,11 @@ import ( "net" "strings" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/guestagent/api" guestagentclient "github.com/lima-vm/lima/pkg/guestagent/api/client" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/sirupsen/logrus" ) var IPv4loopback1 = limayaml.IPv4loopback1 diff --git a/pkg/portfwd/listener.go b/pkg/portfwd/listener.go index edd798f0a75..e4b250dd882 100644 --- a/pkg/portfwd/listener.go +++ b/pkg/portfwd/listener.go @@ -10,8 +10,9 @@ import ( "strings" "sync" - guestagentclient "github.com/lima-vm/lima/pkg/guestagent/api/client" "github.com/sirupsen/logrus" + + guestagentclient "github.com/lima-vm/lima/pkg/guestagent/api/client" ) type ClosableListeners struct { diff --git a/pkg/qemu/entitlementutil/entitlementutil.go b/pkg/qemu/entitlementutil/entitlementutil.go index 63eda14f6a5..6555095dcc9 100644 --- a/pkg/qemu/entitlementutil/entitlementutil.go +++ b/pkg/qemu/entitlementutil/entitlementutil.go @@ -10,10 +10,10 @@ import ( "os/exec" "strings" - "github.com/lima-vm/lima/pkg/uiutil" - "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/uiutil" ) // IsSigned returns an error if the binary is not signed, or the sign is invalid, diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go index 12e148bf3bc..ca775b2d7bc 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/qemu/qemu.go @@ -20,22 +20,22 @@ import ( "strings" "time" - "github.com/lima-vm/lima/pkg/networks/usernet" - "github.com/lima-vm/lima/pkg/osutil" - "github.com/coreos/go-semver/semver" "github.com/digitalocean/go-qemu/qmp" "github.com/digitalocean/go-qemu/qmp/raw" "github.com/docker/go-units" + "github.com/mattn/go-shellwords" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/fileutils" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" + "github.com/lima-vm/lima/pkg/networks/usernet" + "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/mattn/go-shellwords" - "github.com/sirupsen/logrus" ) type Config struct { diff --git a/pkg/qemu/qemu_driver.go b/pkg/qemu/qemu_driver.go index 7ec1fc2b56d..e28b23429d5 100644 --- a/pkg/qemu/qemu_driver.go +++ b/pkg/qemu/qemu_driver.go @@ -24,6 +24,8 @@ import ( "github.com/coreos/go-semver/semver" "github.com/digitalocean/go-qemu/qmp" "github.com/digitalocean/go-qemu/qmp/raw" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/limayaml" @@ -32,7 +34,6 @@ import ( "github.com/lima-vm/lima/pkg/qemu/entitlementutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) type LimaQemuDriver struct { diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go index 0bf390b30ce..81e81e373fe 100644 --- a/pkg/sshutil/sshutil.go +++ b/pkg/sshutil/sshutil.go @@ -20,14 +20,15 @@ import ( "time" "github.com/coreos/go-semver/semver" + "github.com/mattn/go-shellwords" + "github.com/sirupsen/logrus" + "golang.org/x/sys/cpu" + "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/lockutil" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/mattn/go-shellwords" - "github.com/sirupsen/logrus" - "golang.org/x/sys/cpu" ) // Environment variable that allows configuring the command (alias) to execute diff --git a/pkg/store/disk.go b/pkg/store/disk.go index a9bc102cb49..0b5b1224da6 100644 --- a/pkg/store/disk.go +++ b/pkg/store/disk.go @@ -11,6 +11,7 @@ import ( "path/filepath" "github.com/lima-vm/go-qcow2reader" + "github.com/lima-vm/lima/pkg/store/filenames" ) diff --git a/pkg/store/fuzz_test.go b/pkg/store/fuzz_test.go index 511bbdb7410..4654a3d3b88 100644 --- a/pkg/store/fuzz_test.go +++ b/pkg/store/fuzz_test.go @@ -8,8 +8,9 @@ import ( "path/filepath" "testing" - "github.com/lima-vm/lima/pkg/store/filenames" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/store/filenames" ) func FuzzLoadYAMLByFilePath(f *testing.F) { diff --git a/pkg/store/instance.go b/pkg/store/instance.go index c8f71b8a538..b9748c9b9d1 100644 --- a/pkg/store/instance.go +++ b/pkg/store/instance.go @@ -19,6 +19,8 @@ import ( "time" "github.com/docker/go-units" + "github.com/sirupsen/logrus" + hostagentclient "github.com/lima-vm/lima/pkg/hostagent/api/client" instancehostname "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/limayaml" @@ -26,7 +28,6 @@ import ( "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/textutil" "github.com/lima-vm/lima/pkg/version/versionutil" - "github.com/sirupsen/logrus" ) type Status = string diff --git a/pkg/store/instance_test.go b/pkg/store/instance_test.go index 52a189e3a44..5991679cea2 100644 --- a/pkg/store/instance_test.go +++ b/pkg/store/instance_test.go @@ -11,8 +11,9 @@ import ( "strings" "testing" - "github.com/lima-vm/lima/pkg/limayaml" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/limayaml" ) const separator = string(filepath.Separator) diff --git a/pkg/store/store.go b/pkg/store/store.go index 58669dd753a..10561463c40 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containerd/containerd/identifiers" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go index 8ad5cf66896..51fd4a1970a 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocalsharelima/usrlocalsharelima.go @@ -13,9 +13,10 @@ import ( "runtime" "sync" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/debugutil" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/sirupsen/logrus" ) // executableViaArgs0 returns the absolute path to the executable used to start this process. diff --git a/pkg/vz/disk.go b/pkg/vz/disk.go index 596bc1bab4d..3fff17c48fd 100644 --- a/pkg/vz/disk.go +++ b/pkg/vz/disk.go @@ -11,6 +11,7 @@ import ( "path/filepath" "github.com/docker/go-units" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/fileutils" "github.com/lima-vm/lima/pkg/iso9660util" diff --git a/pkg/vz/network_darwin.go b/pkg/vz/network_darwin.go index 39f0cb90a12..cb31e8f7eba 100644 --- a/pkg/vz/network_darwin.go +++ b/pkg/vz/network_darwin.go @@ -16,7 +16,6 @@ import ( "time" "github.com/balajiv113/fd" - "github.com/sirupsen/logrus" ) diff --git a/pkg/vz/rosetta_directory_share_arm64.go b/pkg/vz/rosetta_directory_share_arm64.go index a692dc9f262..6e7fdbfe2e7 100644 --- a/pkg/vz/rosetta_directory_share_arm64.go +++ b/pkg/vz/rosetta_directory_share_arm64.go @@ -10,8 +10,9 @@ import ( "github.com/Code-Hex/vz/v3" "github.com/coreos/go-semver/semver" - "github.com/lima-vm/lima/pkg/osutil" "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/osutil" ) func createRosettaDirectoryShareConfiguration() (*vz.VirtioFileSystemDeviceConfiguration, error) { diff --git a/pkg/vz/vm_darwin.go b/pkg/vz/vm_darwin.go index 407af20c3d5..1f21cf448b0 100644 --- a/pkg/vz/vm_darwin.go +++ b/pkg/vz/vm_darwin.go @@ -22,6 +22,8 @@ import ( "github.com/docker/go-units" "github.com/lima-vm/go-qcow2reader" "github.com/lima-vm/go-qcow2reader/image/raw" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" @@ -31,7 +33,6 @@ import ( "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) // diskImageCachingMode is set to DiskImageCachingModeCached so as to avoid disk corruption on ARM: diff --git a/pkg/vz/vz_driver_darwin.go b/pkg/vz/vz_driver_darwin.go index c131e3c1b6a..812eaeb6a42 100644 --- a/pkg/vz/vz_driver_darwin.go +++ b/pkg/vz/vz_driver_darwin.go @@ -16,7 +16,6 @@ import ( "github.com/Code-Hex/vz/v3" "github.com/coreos/go-semver/semver" - "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" diff --git a/pkg/wsl2/fs.go b/pkg/wsl2/fs.go index c116396dabe..8a3060ac924 100644 --- a/pkg/wsl2/fs.go +++ b/pkg/wsl2/fs.go @@ -9,10 +9,11 @@ import ( "os" "path/filepath" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/fileutils" "github.com/lima-vm/lima/pkg/store/filenames" - "github.com/sirupsen/logrus" ) // EnsureFs downloads the root fs. diff --git a/pkg/wsl2/vm_windows.go b/pkg/wsl2/vm_windows.go index d0b537e33b6..531de6c0116 100644 --- a/pkg/wsl2/vm_windows.go +++ b/pkg/wsl2/vm_windows.go @@ -13,11 +13,12 @@ import ( "strconv" "strings" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/textutil" - "github.com/sirupsen/logrus" ) // startVM calls WSL to start a VM. diff --git a/pkg/wsl2/wsl_driver_windows.go b/pkg/wsl2/wsl_driver_windows.go index 318f80b3819..479aaf0ffd7 100644 --- a/pkg/wsl2/wsl_driver_windows.go +++ b/pkg/wsl2/wsl_driver_windows.go @@ -11,13 +11,14 @@ import ( "github.com/Microsoft/go-winio" "github.com/Microsoft/go-winio/pkg/guid" + "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/freeport" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/reflectutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/windows" - "github.com/sirupsen/logrus" ) var knownYamlProperties = []string{ From c2f29057332c7478163f8e5e0e792c99d3e5c061 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 26 May 2025 15:43:28 +0900 Subject: [PATCH 08/91] go.mod: drop dependency on github.com/containerd/containerd Silences govulncheck false positives GO-2025-3699 and GO-2025-3701. The dependency was also problematic because it looked like as if it was the actual daemon version running in the VM. Copied https://github.com/containerd/containerd/tree/v2.1.1/pkg/identifiers with the modification to drop the dependency on other containerd packages. The NOTICE file was added for compliance of the section 4(d) of the Apache License Version 2.0. Signed-off-by: Akihiro Suda --- NOTICE | 26 +++++++++++ go.mod | 2 - go.sum | 4 -- pkg/cidata/template.go | 3 +- pkg/identifiers/validate.go | 78 ++++++++++++++++++++++++++++++++ pkg/identifiers/validate_test.go | 74 ++++++++++++++++++++++++++++++ pkg/limatmpl/locator.go | 2 +- pkg/limayaml/validate.go | 2 +- pkg/limayaml/validate_test.go | 2 +- pkg/store/store.go | 3 +- 10 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 NOTICE create mode 100644 pkg/identifiers/validate.go create mode 100644 pkg/identifiers/validate_test.go diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000000..3709ce1aecd --- /dev/null +++ b/NOTICE @@ -0,0 +1,26 @@ +Lima +Copyright The Lima Authors. + +This project contains portions of other projects that are licensed under the terms of Apache License 2.0. +The NOTICE files of those projects are replicated here for compliance of the section 4(d) of the license. + +=== https://github.com/containerd/containerd === +https://github.com/containerd/containerd/blob/v2.1.1/LICENSE +https://github.com/containerd/containerd/blob/v2.1.1/NOTICE + +> Docker +> Copyright 2012-2015 Docker, Inc. +> +> This product includes software developed at Docker, Inc. (https://www.docker.com). +> +> The following is courtesy of our legal counsel: +> +> +> Use and transfer of Docker may be subject to certain restrictions by the +> United States and other governments. +> It is your responsibility to ensure that your use and/or transfer does not +> violate applicable laws. +> +> For more information, please see https://www.bis.doc.gov +> +> See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/go.mod b/go.mod index 9faca1a4e57..2ebd0f7b1ca 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/apparentlymart/go-cidr v1.1.0 github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e github.com/cheggaaa/pb/v3 v3.1.7 // gomodjail:unconfined - github.com/containerd/containerd v1.7.27 github.com/containerd/continuity v0.4.5 github.com/containers/gvisor-tap-vsock v0.8.6 // gomodjail:unconfined github.com/coreos/go-semver v0.3.1 @@ -63,7 +62,6 @@ require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect diff --git a/go.sum b/go.sum index 73daa18ee4f..7d4db8a3c6b 100644 --- a/go.sum +++ b/go.sum @@ -32,12 +32,8 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI= github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ= -github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= -github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containers/gvisor-tap-vsock v0.8.6 h1:9SeAXK+K2o36CtrgYk6zRXbU3zrayjvkrI8b7/O6u5A= diff --git a/pkg/cidata/template.go b/pkg/cidata/template.go index 2874c1c4f8a..80622def553 100644 --- a/pkg/cidata/template.go +++ b/pkg/cidata/template.go @@ -11,8 +11,7 @@ import ( "io/fs" "path" - "github.com/containerd/containerd/identifiers" - + "github.com/lima-vm/lima/pkg/identifiers" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/textutil" ) diff --git a/pkg/identifiers/validate.go b/pkg/identifiers/validate.go new file mode 100644 index 00000000000..eb4f3802d69 --- /dev/null +++ b/pkg/identifiers/validate.go @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +// From https://github.com/containerd/containerd/blob/v2.1.1/pkg/identifiers/validate.go +// SPDX-FileCopyrightText: Copyright The containerd Authors +// LICENSE: https://github.com/containerd/containerd/blob/v2.1.1/LICENSE +// NOTICE: https://github.com/containerd/containerd/blob/v2.1.1/NOTICE + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package identifiers provides common validation for identifiers and keys +// across Lima (originally from containerd). +// +// Identifiers in Lima must be a alphanumeric, allowing limited +// underscores, dashes and dots. +// +// While the character set may be expanded in the future, identifiers +// are guaranteed to be safely used as filesystem path components. +package identifiers + +import ( + "errors" + "fmt" + "regexp" +) + +const ( + maxLength = 76 + alphanum = `[A-Za-z0-9]+` + separators = `[._-]` +) + +// identifierRe defines the pattern for valid identifiers. +var identifierRe = regexp.MustCompile(reAnchor(alphanum + reGroup(separators+reGroup(alphanum)) + "*")) + +// Validate returns nil if the string s is a valid identifier. +// +// Identifiers are similar to the domain name rules according to RFC 1035, section 2.3.1. However +// rules in this package are relaxed to allow numerals to follow period (".") and mixed case is +// allowed. +// +// In general identifiers that pass this validation should be safe for use as filesystem path components. +func Validate(s string) error { + if s == "" { + return errors.New("identifier must not be empty") + } + + if len(s) > maxLength { + return fmt.Errorf("identifier %q greater than maximum length (%d characters)", s, maxLength) + } + + if !identifierRe.MatchString(s) { + return fmt.Errorf("identifier %q must match %v", s, identifierRe) + } + return nil +} + +func reGroup(s string) string { + return `(?:` + s + `)` +} + +func reAnchor(s string) string { + return `^` + s + `$` +} diff --git a/pkg/identifiers/validate_test.go b/pkg/identifiers/validate_test.go new file mode 100644 index 00000000000..98543bc2fa0 --- /dev/null +++ b/pkg/identifiers/validate_test.go @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +// From https://github.com/containerd/containerd/blob/v2.1.1/pkg/identifiers/validate_test.go +// SPDX-FileCopyrightText: Copyright The containerd Authors +// LICENSE: https://github.com/containerd/containerd/blob/v2.1.1/LICENSE +// NOTICE: https://github.com/containerd/containerd/blob/v2.1.1/NOTICE + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package identifiers + +import ( + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestValidIdentifiers(t *testing.T) { + for _, input := range []string{ + "default", + "Default", + t.Name(), + "default-default", + "containerd.io", + "foo.boo", + "swarmkit.docker.io", + "0912341234", + "task.0.0123456789", + "container.system-75-f19a.00", + "underscores_are_allowed", + strings.Repeat("a", maxLength), + } { + t.Run(input, func(t *testing.T) { + assert.NilError(t, Validate(input)) + }) + } +} + +func TestInvalidIdentifiers(t *testing.T) { + for _, input := range []string{ + "", + ".foo..foo", + "foo/foo", + "foo/..", + "foo..foo", + "foo.-boo", + "-foo.boo", + "foo.boo-", + "but__only_tasteful_underscores", + "zn--e9.org", // or something like it! + "default--default", + strings.Repeat("a", maxLength+1), + } { + t.Run(input, func(t *testing.T) { + assert.ErrorContains(t, Validate(input), "") + }) + } +} diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go index 6f952b2007a..328b79b3dda 100644 --- a/pkg/limatmpl/locator.go +++ b/pkg/limatmpl/locator.go @@ -16,9 +16,9 @@ import ( "runtime" "strings" - "github.com/containerd/containerd/identifiers" "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/identifiers" "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/templatestore" diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index eeef301c90a..750383e2c51 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -17,11 +17,11 @@ import ( "strings" "unicode" - "github.com/containerd/containerd/identifiers" "github.com/coreos/go-semver/semver" "github.com/docker/go-units" "github.com/sirupsen/logrus" + "github.com/lima-vm/lima/pkg/identifiers" "github.com/lima-vm/lima/pkg/localpathutil" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index 7e90bc8224b..ed2233822b5 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -86,7 +86,7 @@ additionalDisks: assert.NilError(t, err) err = Validate(y, false) - assert.Error(t, err, "field `additionalDisks[0].name is invalid`: identifier must not be empty: invalid argument") + assert.Error(t, err, "field `additionalDisks[0].name is invalid`: identifier must not be empty") } func TestValidateParamName(t *testing.T) { diff --git a/pkg/store/store.go b/pkg/store/store.go index 10561463c40..fd3d160a159 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -10,8 +10,7 @@ import ( "path/filepath" "strings" - "github.com/containerd/containerd/identifiers" - + "github.com/lima-vm/lima/pkg/identifiers" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" From 27eee49546afd3f01c26e223017b0457e537b7d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 01:24:26 +0000 Subject: [PATCH 09/91] build(deps): bump google.golang.org/grpc from 1.72.1 to 1.72.2 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.1 to 1.72.2. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.72.1...v1.72.2) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.72.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2ebd0f7b1ca..fe9b2ee3f58 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( golang.org/x/sync v0.14.0 golang.org/x/sys v0.33.0 // gomodjail:unconfined golang.org/x/text v0.25.0 - google.golang.org/grpc v1.72.1 + google.golang.org/grpc v1.72.2 google.golang.org/protobuf v1.36.6 // gomodjail:unconfined gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gotest.tools/v3 v3.5.2 diff --git a/go.sum b/go.sum index 7d4db8a3c6b..142be491d51 100644 --- a/go.sum +++ b/go.sum @@ -408,8 +408,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From aa409d13c420da77657dc56f80a6c681d014bdc9 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Mon, 26 May 2025 18:49:41 -0700 Subject: [PATCH 10/91] Change package name of instance/hostname from instance to hostname Signed-off-by: Jan Dubois --- pkg/cidata/cidata.go | 4 ++-- pkg/hostagent/hostagent.go | 6 +++--- pkg/instance/hostname/hostname.go | 6 +++--- pkg/instance/hostname/hostname_test.go | 12 ++++++------ pkg/limayaml/defaults.go | 4 ++-- pkg/sshutil/format.go | 4 ++-- pkg/store/instance.go | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index dba3cbc9368..8439ca022f9 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -23,7 +23,7 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/debugutil" - instance "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/localpathutil" @@ -124,7 +124,7 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L Debug: debugutil.Debug, BootScripts: bootScripts, Name: name, - Hostname: instance.HostnameFromInstName(name), // TODO: support customization + Hostname: hostname.FromInstName(name), // TODO: support customization User: *instConfig.User.Name, Comment: *instConfig.User.Comment, Home: *instConfig.User.Home, diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index ee8b42bddb8..a6e293e11a2 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -34,7 +34,7 @@ import ( hostagentapi "github.com/lima-vm/lima/pkg/hostagent/api" "github.com/lima-vm/lima/pkg/hostagent/dns" "github.com/lima-vm/lima/pkg/hostagent/events" - instance "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" @@ -288,8 +288,8 @@ func (a *HostAgent) Run(ctx context.Context) error { if limayaml.FirstUsernetIndex(a.instConfig) == -1 && *a.instConfig.HostResolver.Enabled { hosts := a.instConfig.HostResolver.Hosts hosts["host.lima.internal"] = networks.SlirpGateway - hostname := instance.HostnameFromInstName(a.instName) // TODO: support customization - hosts[hostname] = networks.SlirpIPAddress + name := hostname.FromInstName(a.instName) // TODO: support customization + hosts[name] = networks.SlirpIPAddress srvOpts := dns.ServerOptions{ UDPPort: a.udpDNSLocalPort, TCPPort: a.tcpDNSLocalPort, diff --git a/pkg/instance/hostname/hostname.go b/pkg/instance/hostname/hostname.go index 38756c8543d..c67f0ff9d17 100644 --- a/pkg/instance/hostname/hostname.go +++ b/pkg/instance/hostname/hostname.go @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package instance +package hostname import "strings" -// HostnameFromInstName generates a hostname from an instance name by prefixing +// FromInstName generates a hostname from an instance name by prefixing // it with "lima-" and replacing all dots and underscores with dashes. // E.g. "my_example.com" becomes "lima-my-example-com". -func HostnameFromInstName(instName string) string { +func FromInstName(instName string) string { s := strings.ReplaceAll(instName, ".", "-") s = strings.ReplaceAll(s, "_", "-") return "lima-" + s diff --git a/pkg/instance/hostname/hostname_test.go b/pkg/instance/hostname/hostname_test.go index 8173d62d2a1..e702167e11f 100644 --- a/pkg/instance/hostname/hostname_test.go +++ b/pkg/instance/hostname/hostname_test.go @@ -1,18 +1,18 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package instance_test +package hostname_test import ( "testing" "gotest.tools/v3/assert" - instance "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" ) -func TestHostnameFromInstName(t *testing.T) { - assert.Equal(t, instance.HostnameFromInstName("default"), "lima-default") - assert.Equal(t, instance.HostnameFromInstName("ubuntu-24.04"), "lima-ubuntu-24-04") - assert.Equal(t, instance.HostnameFromInstName("foo_bar.baz"), "lima-foo-bar-baz") +func TestFromInstName(t *testing.T) { + assert.Equal(t, hostname.FromInstName("default"), "lima-default") + assert.Equal(t, hostname.FromInstName("ubuntu-24.04"), "lima-ubuntu-24-04") + assert.Equal(t, hostname.FromInstName("foo_bar.baz"), "lima-foo-bar-baz") } diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index 2351287a60e..a2193a28389 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -28,7 +28,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/sys/cpu" - instance "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/localpathutil" . "github.com/lima-vm/lima/pkg/must" @@ -944,7 +944,7 @@ func executeGuestTemplate(format, instDir string, user User, param map[string]st name := filepath.Base(instDir) data := map[string]any{ "Name": name, - "Hostname": instance.HostnameFromInstName(name), // TODO: support customization + "Hostname": hostname.FromInstName(name), // TODO: support customization "UID": *user.UID, "User": *user.Name, "Home": *user.Home, diff --git a/pkg/sshutil/format.go b/pkg/sshutil/format.go index 652b542931b..0856965050d 100644 --- a/pkg/sshutil/format.go +++ b/pkg/sshutil/format.go @@ -8,7 +8,7 @@ import ( "io" "strings" - instance "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" ) // FormatT specifies the format type. @@ -62,7 +62,7 @@ func quoteOption(o string) string { // Format formats the ssh options. func Format(w io.Writer, sshPath, instName string, format FormatT, opts []string) error { - fakeHostname := instance.HostnameFromInstName(instName) // TODO: support customization + fakeHostname := hostname.FromInstName(instName) // TODO: support customization switch format { case FormatCmd: args := []string{sshPath} diff --git a/pkg/store/instance.go b/pkg/store/instance.go index b9748c9b9d1..19b4b2270dc 100644 --- a/pkg/store/instance.go +++ b/pkg/store/instance.go @@ -22,7 +22,7 @@ import ( "github.com/sirupsen/logrus" hostagentclient "github.com/lima-vm/lima/pkg/hostagent/api/client" - instancehostname "github.com/lima-vm/lima/pkg/instance/hostname" + "github.com/lima-vm/lima/pkg/instance/hostname" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" @@ -74,7 +74,7 @@ func Inspect(instName string) (*Instance, error) { inst := &Instance{ Name: instName, // TODO: support customizing hostname - Hostname: instancehostname.HostnameFromInstName(instName), + Hostname: hostname.FromInstName(instName), Status: StatusUnknown, } // InstanceDir validates the instName but does not check whether the instance exists From 5ce662d52ce88098f6bc6688c901a4031536f762 Mon Sep 17 00:00:00 2001 From: Songpon Srisawai Date: Mon, 26 May 2025 09:35:21 +0700 Subject: [PATCH 11/91] Add conf.d for lima-guestagent service Co-authored-by: Jan Dubois Signed-off-by: Songpon Srisawai --- pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh b/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh index d48b2846cc6..f5089144a65 100644 --- a/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh +++ b/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh @@ -22,6 +22,9 @@ install -m 755 "${LIMA_CIDATA_MNT}"/lima-guestagent "${LIMA_CIDATA_GUEST_INSTALL # Launch the guestagent service if [ -f /sbin/openrc-run ]; then + # Convert .env to conf.d by wrapping values in double quotes. + # Split the variable and value at the first "=" to handle cases where the value contains additional "=" characters. + sed -E 's/^([^=]+)=(.*)/\1="\2"/' "${LIMA_CIDATA_MNT}/lima.env" >"/etc/conf.d/lima-guestagent" # Install the openrc lima-guestagent service script cat >/etc/init.d/lima-guestagent <<'EOF' #!/sbin/openrc-run From d3a57410bacc0b301f9996c35c898a5ceb60ad37 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Tue, 27 May 2025 12:40:41 +0300 Subject: [PATCH 12/91] chore: Fix typos in comments Signed-off-by: Oleksandr Redko --- Makefile | 4 ++-- pkg/nativeimgutil/nativeimgutil.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a6aaea1e886..c9da565c716 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ help: @echo ' help-variables - Show Makefile variables' @echo ' help-targets - Show additional Makefile targets' -.PHONY: help-varaibles +.PHONY: help-variables help-variables: @echo '# Variables that can be overridden.' @echo @@ -318,7 +318,7 @@ additional-guestagents: $(ADDITIONAL_GUESTAGENTS) %-guestagent: @[ "$(findstring $(*),$(LINUX_GUESTAGENT_ARCHS))" == "$(*)" ] && make $(call guestagent_path,LINUX,$*) -# environment variables for linx-guestagent. these variable are used for checking force build. +# environment variables for linux-guestagent. these variable are used for checking force build. ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)aarch64 = CGO_ENABLED=0 GOOS=linux GOARCH=arm64 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)armv7l = CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)ppc64le = CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le diff --git a/pkg/nativeimgutil/nativeimgutil.go b/pkg/nativeimgutil/nativeimgutil.go index 094a5571168..b4ccb0eab0e 100644 --- a/pkg/nativeimgutil/nativeimgutil.go +++ b/pkg/nativeimgutil/nativeimgutil.go @@ -23,7 +23,7 @@ import ( "github.com/lima-vm/lima/pkg/progressbar" ) -// Disk image size must be aigned to sector size. Qemu block layer is rounding +// Disk image size must be aligned to sector size. Qemu block layer is rounding // up the size to 512 bytes. Apple virtualization framework reject disks not // aligned to 512 bytes. const sectorSize = 512 From e4d7f95861796b5b5fbb5ce7ac7d320f5c4374a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 01:43:56 +0000 Subject: [PATCH 13/91] build(deps): bump github.com/goccy/go-yaml from 1.17.1 to 1.18.0 Bumps [github.com/goccy/go-yaml](https://github.com/goccy/go-yaml) from 1.17.1 to 1.18.0. - [Release notes](https://github.com/goccy/go-yaml/releases) - [Changelog](https://github.com/goccy/go-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/goccy/go-yaml/compare/v1.17.1...v1.18.0) --- updated-dependencies: - dependency-name: github.com/goccy/go-yaml dependency-version: 1.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fe9b2ee3f58..391502a9020 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/docker/go-units v0.5.0 github.com/elastic/go-libaudit/v2 v2.6.2 github.com/foxcpp/go-mockdns v1.1.0 - github.com/goccy/go-yaml v1.17.1 + github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 github.com/google/yamlfmt v0.17.0 github.com/invopop/jsonschema v0.13.0 diff --git a/go.sum b/go.sum index 142be491d51..1a513096797 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,8 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= -github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= From 364140ded8863b2a68c6e82cee534d954eb70721 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Sat, 31 May 2025 22:41:29 -0700 Subject: [PATCH 14/91] Fix installation of dummy WSL2 distro wsl --import no longer accepts an empty tarball, but checks that the tarball contains at least /bin/sh and /etc. https://github.com/microsoft/WSL/blob/9cd3438/src/linux/init/main.cpp#L2621-L2629 Signed-off-by: Jan Dubois --- .github/workflows/test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1bdc0ab66a6..3ec19a47da1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -148,8 +148,15 @@ jobs: # otherwise `wsl --list --verbose` (called from Lima) fails: # https://github.com/lima-vm/lima/pull/1826#issuecomment-1729993334 # The distro image itself is not consumed by Lima. + # Starting with WSL2 version 2.5.7.0 the distro will be rejected + # if it doesn't contain /bin/sh and /etc. # ------------------------------------------------------------------ - wsl --import dummy $env:TEMP nul + mkdir dummy + mkdir dummy\bin + mkdir dummy\etc + echo "" >dummy\bin\sh + tar -cf dummy.tar --format ustar -C dummy . + wsl --import dummy $env:TEMP dummy.tar wsl --list --verbose - name: Set gitconfig run: | From 1acd7c0c5c0033fa49ba8861e50aafff475e5d90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 01:42:21 +0000 Subject: [PATCH 15/91] build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.1 to 2.4.2. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/f49aabe0b5af0936a0987cfb85d86b75731b0186...05b42c624433fc40578a4040d5cf5e36ddca8cde) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8d9c7438bd3..b83dfae3dde 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -32,7 +32,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 with: results_file: results.sarif results_format: sarif From f1650ab9be660822daaf0ae7cb93e8b17e24c873 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 01:48:02 +0000 Subject: [PATCH 16/91] build(deps): bump github/codeql-action from 3.28.18 to 3.28.19 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.18 to 3.28.19. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/ff0a06e83cb2de871e5a09832bc6a81e7276941f...fca7ace96b7d713c7035871441bd52efbe39e27e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.19 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yaml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 12cc69f22e2..6cd1ef292fe 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Initialize CodeQL - uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -48,6 +48,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index b83dfae3dde..06056b9f38f 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: sarif_file: results.sarif From cd5cf72b111bd056a7d5836abd88a9be2b1756dc Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Tue, 27 May 2025 12:56:08 +0300 Subject: [PATCH 17/91] Run golangci-lint on macOS and Windows; fix lint issues Signed-off-by: Oleksandr Redko --- .github/workflows/test.yml | 32 +++++++++++++++++++++++++++----- pkg/lockutil/lockutil_windows.go | 11 ++++------- pkg/osutil/osutil_windows.go | 8 ++++---- pkg/store/instance_windows.go | 4 ++-- pkg/vz/vz_driver_darwin.go | 1 - pkg/windows/registry_windows.go | 8 ++++---- pkg/wsl2/wsl_driver_windows.go | 3 +-- 7 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ec19a47da1..4ee1808fa3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,11 +39,6 @@ jobs: sudo apt-get install -y protobuf-compiler - name: Verify generated files run: make install-tools generate check-generated - - name: Run golangci-lint - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 - with: - version: v2.1.0 - args: --verbose - name: Run yamllint run: yamllint . - name: Install shellcheck @@ -74,6 +69,33 @@ jobs: - name: Check license boilerplates run: ltag -t ./hack/ltag --check -v + lint-go: + name: "Lint Go" + timeout-minutes: 30 + strategy: + matrix: + runs-on: [ubuntu-24.04, macos-15, windows-2022] + runs-on: ${{ matrix.runs-on }} + steps: + - name: Force git to use LF + # This step is required on Windows to work around golangci-lint issues with formatters. See https://github.com/golangci/golangci-lint/discussions/5840 + # TODO: replace with a checkout option when https://github.com/actions/checkout/issues/226 is implemented + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: 1.24.x + - name: Run golangci-lint + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 + with: + version: v2.1 + args: --verbose + security: name: "Vulncheck" runs-on: ubuntu-24.04 diff --git a/pkg/lockutil/lockutil_windows.go b/pkg/lockutil/lockutil_windows.go index 6653ae3f6d4..d526ba8bb77 100644 --- a/pkg/lockutil/lockutil_windows.go +++ b/pkg/lockutil/lockutil_windows.go @@ -29,18 +29,15 @@ import ( "github.com/sirupsen/logrus" ) -// LockFile modified from https://github.com/boltdb/bolt/blob/v1.3.1/bolt_windows.go using MIT +// LockFile modified from https://github.com/boltdb/bolt/blob/v1.3.1/bolt_windows.go using MIT. var ( modkernel32 = syscall.NewLazyDLL("kernel32.dll") procLockFileEx = modkernel32.NewProc("LockFileEx") procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") ) -const ( - // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx - LOCKFILE_EXCLUSIVE_LOCK = 0x00000002 - LOCKFILE_FAIL_IMMEDIATELY = 0x00000001 -) +// LOCKFILE_EXCLUSIVE_LOCK from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx +const flagLockfileExclusiveLock = 0x00000002 func WithDirLock(dir string, fn func() error) error { dirFile, err := os.OpenFile(dir+".lock", os.O_CREATE, 0o644) @@ -50,7 +47,7 @@ func WithDirLock(dir string, fn func() error) error { defer dirFile.Close() if err := lockFileEx( syscall.Handle(dirFile.Fd()), // hFile - LOCKFILE_EXCLUSIVE_LOCK, // dwFlags + flagLockfileExclusiveLock, // dwFlags 0, // dwReserved 1, // nNumberOfBytesToLockLow 0, // nNumberOfBytesToLockHigh diff --git a/pkg/osutil/osutil_windows.go b/pkg/osutil/osutil_windows.go index ead56d70863..d16de1f3964 100644 --- a/pkg/osutil/osutil_windows.go +++ b/pkg/osutil/osutil_windows.go @@ -16,7 +16,7 @@ import ( // UnixPathMax is the value of UNIX_PATH_MAX. const UnixPathMax = 108 -// Stat is a selection of syscall.Stat_t +// Stat is a selection of syscall.Stat_t. type Stat struct { Uid uint32 Gid uint32 @@ -38,8 +38,8 @@ func SysKill(pid int, _ Signal) error { return windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, uint32(pid)) } -func Dup2(oldfd int, newfd syscall.Handle) (err error) { - return fmt.Errorf("unimplemented") +func Dup2(_ int, _ syscall.Handle) error { + return errors.New("unimplemented") } func SignalName(sig os.Signal) string { @@ -53,6 +53,6 @@ func SignalName(sig os.Signal) string { } } -func Sysctl(name string) (string, error) { +func Sysctl(_ string) (string, error) { return "", errors.New("sysctl: unimplemented on Windows") } diff --git a/pkg/store/instance_windows.go b/pkg/store/instance_windows.go index 208bbfb8155..4f7b317175a 100644 --- a/pkg/store/instance_windows.go +++ b/pkg/store/instance_windows.go @@ -75,7 +75,7 @@ func GetWslStatus(instName string) (string, error) { return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, string(out)) } - if len(out) == 0 { + if out == "" { return StatusBroken, fmt.Errorf("failed to read instance state for instance %q, try running `wsl --list --verbose` to debug, err: %w", instName, err) } @@ -116,6 +116,6 @@ func GetWslStatus(instName string) (string, error) { return instState, nil } -func GetSSHAddress(instName string) (string, error) { +func GetSSHAddress(_ string) (string, error) { return "127.0.0.1", nil } diff --git a/pkg/vz/vz_driver_darwin.go b/pkg/vz/vz_driver_darwin.go index 812eaeb6a42..ee1b34b910f 100644 --- a/pkg/vz/vz_driver_darwin.go +++ b/pkg/vz/vz_driver_darwin.go @@ -202,7 +202,6 @@ func (l *LimaVzDriver) RunGUI() error { if l.CanRunGUI() { return l.machine.StartGraphicApplication(1920, 1200) } - //nolint:revive // error-strings return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "vz", *l.Instance.Config.Video.Display) } diff --git a/pkg/windows/registry_windows.go b/pkg/windows/registry_windows.go index 9ba599b3347..1f2c88bf880 100644 --- a/pkg/windows/registry_windows.go +++ b/pkg/windows/registry_windows.go @@ -146,7 +146,7 @@ func GetDistroID(name string) (string, error) { } // GetRandomFreeVSockPort gets a list of all registered VSock ports and returns a non-registered port. -func GetRandomFreeVSockPort(min, max int) (int, error) { +func GetRandomFreeVSockPort(minPort, maxPort int) (int, error) { rootKey, err := getGuestCommunicationServicesKey(false) if err != nil { return 0, err @@ -160,18 +160,18 @@ func GetRandomFreeVSockPort(min, max int) (int, error) { type pair struct{ v, offset int } tree := make([]pair, 1, len(used)+1) - tree[0] = pair{0, min} + tree[0] = pair{0, minPort} sort.Ints(used) for i, v := range used { if tree[len(tree)-1].v+tree[len(tree)-1].offset == v { tree[len(tree)-1].offset++ } else { - tree = append(tree, pair{v - min - i, min + i + 1}) + tree = append(tree, pair{v - minPort - i, minPort + i + 1}) } } - v := rand.IntN(max - min + 1 - len(used)) + v := rand.IntN(maxPort - minPort + 1 - len(used)) for len(tree) > 1 { m := len(tree) / 2 diff --git a/pkg/wsl2/wsl_driver_windows.go b/pkg/wsl2/wsl_driver_windows.go index 479aaf0ffd7..4ad7d472452 100644 --- a/pkg/wsl2/wsl_driver_windows.go +++ b/pkg/wsl2/wsl_driver_windows.go @@ -152,10 +152,9 @@ func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) { return errCh, err } -// Requires WSLg, which requires specific version of WSL2 to be installed. +// CanRunGUI requires WSLg, which requires specific version of WSL2 to be installed. // TODO: Add check and add support for WSLg (instead of VNC) to hostagent. func (l *LimaWslDriver) CanRunGUI() bool { - // return *l.InstConfig.Video.Display == "wsl" return false } From 90a8cf71048cf6d9236b6ac5e577df75154d01f3 Mon Sep 17 00:00:00 2001 From: "re:fi.64" Date: Sun, 1 Jun 2025 20:00:31 -0500 Subject: [PATCH 18/91] templates: Add almalinux-10 This adds a template for the relatively new AlmaLinux 10 release and updates the `almalinux.yaml` symlink. Signed-off-by: re:fi.64 --- hack/update-template-almalinux.sh | 4 ++-- templates/README.md | 3 ++- templates/_images/almalinux-10.yaml | 23 +++++++++++++++++++++++ templates/almalinux-10.yaml | 5 +++++ templates/almalinux.yaml | 2 +- 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 templates/_images/almalinux-10.yaml create mode 100644 templates/almalinux-10.yaml diff --git a/hack/update-template-almalinux.sh b/hack/update-template-almalinux.sh index 8f3076aaea9..c6210308dc3 100755 --- a/hack/update-template-almalinux.sh +++ b/hack/update-template-almalinux.sh @@ -59,7 +59,7 @@ function almalinux_url_spec_from_location() { jq_filter='capture( "^https://repo\\.almalinux\\.org/almalinux/(?\\d+(\\.\\d+)?)/cloud/(?[^/]+)/images/" + "AlmaLinux-(?\\d+)-(?.*)-" + - "(latest|(?\\d+\\.\\d+)-(?\\d{8}))\\.(?[^.]+).(?.*)$" + "(latest|(?\\d+\\.\\d+)-(?\\d{8})(?:\\.\\d+)?)\\.(?[^.]+).(?.*)$" ;"x") ' url_spec=$(jq -e -r "${jq_filter}" <<<"\"${location}\"") @@ -112,7 +112,7 @@ function almalinux_latest_image_entry_for_url_spec() { capture( "^AlmaLinux-\($spec.major_version)-\($spec.target_vendor)-" + "(?\($spec.major_version)\\.\\d+)-" + - "(?\\d{8})\\.\($spec.arch)\\.\($spec.file_extension)$" + "(?\\d{8}(?:\\.\\d)?)\\.\($spec.arch)\\.\($spec.file_extension)$" ;"x" ) | .version_number_array = ([.major_minor_version | scan("\\d+") | tonumber]) diff --git a/templates/README.md b/templates/README.md index 33eccb8e2fd..da51eeedda5 100644 --- a/templates/README.md +++ b/templates/README.md @@ -12,7 +12,8 @@ Default: [`default`](./default.yaml) (⭐Ubuntu, with containerd/nerdctl) Distro: - [`almalinux-8`](./almalinux-8.yaml): AlmaLinux 8 -- [`almalinux-9`](./almalinux-9.yaml), `almalinux.yaml`: AlmaLinux 9 +- [`almalinux-9`](./almalinux-9.yaml): AlmaLinux 9 +- [`almalinux-10`](./almalinux-10.yaml), `almalinux.yaml`: AlmaLinux 10 - [`almalinux-kitten-10`](./almalinux-kitten-10.yaml), `almalinux-kitten.yaml`: AlmaLinux Kitten 10 - [`alpine`](./alpine.yaml): ☆Alpine Linux - [`alpine-iso`](./alpine-iso.yaml): ☆Alpine Linux (ISO9660 image). Compatible with the `alpine` template used in Lima prior to v1.0. diff --git a/templates/_images/almalinux-10.yaml b/templates/_images/almalinux-10.yaml new file mode 100644 index 00000000000..6f49d8a9ec8 --- /dev/null +++ b/templates/_images/almalinux-10.yaml @@ -0,0 +1,23 @@ +images: +- location: "https://repo.almalinux.org/almalinux/10.0/cloud/x86_64/images/AlmaLinux-10-GenericCloud-10.0-20250528.0.x86_64.qcow2" + arch: "x86_64" + digest: "sha256:9c528c5895c7fb5fc906add749d48057dbba4a4258a708c224aa3ef60fda14bd" +- location: "https://repo.almalinux.org/almalinux/10.0/cloud/aarch64/images/AlmaLinux-10-GenericCloud-10.0-20250528.0.aarch64.qcow2" + arch: "aarch64" + digest: "sha256:34f255fab5d82f5470b173016cf7265282f09fc36a0c50fde72c9a7166c450ea" +- location: "https://repo.almalinux.org/almalinux/10.0/cloud/s390x/images/AlmaLinux-10-GenericCloud-10.0-20250528.0.s390x.qcow2" + arch: "s390x" + digest: "sha256:a867b0ee9b9e537aa2d599430ee9be0808fc66f723dbf5937b204bfeb449b207" +# Fallback to the latest release image. +# Hint: run `limactl prune` to invalidate the cache + +- location: https://repo.almalinux.org/almalinux/10/cloud/x86_64/images/AlmaLinux-10-GenericCloud-latest.x86_64.qcow2 + arch: x86_64 + +- location: https://repo.almalinux.org/almalinux/10/cloud/aarch64/images/AlmaLinux-10-GenericCloud-latest.aarch64.qcow2 + arch: aarch64 + +- location: https://repo.almalinux.org/almalinux/10/cloud/s390x/images/AlmaLinux-10-GenericCloud-latest.s390x.qcow2 + arch: s390x + +mountTypesUnsupported: [9p] diff --git a/templates/almalinux-10.yaml b/templates/almalinux-10.yaml new file mode 100644 index 00000000000..eedc6322a9e --- /dev/null +++ b/templates/almalinux-10.yaml @@ -0,0 +1,5 @@ +minimumLimaVersion: 1.1.0 + +base: +- template://_images/almalinux-10 +- template://_default/mounts diff --git a/templates/almalinux.yaml b/templates/almalinux.yaml index 059f1143435..184e0561552 120000 --- a/templates/almalinux.yaml +++ b/templates/almalinux.yaml @@ -1 +1 @@ -almalinux-9.yaml \ No newline at end of file +almalinux-10.yaml \ No newline at end of file From 058fe530ea754749a384438baedfbcbcd1e69055 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 01:57:48 +0000 Subject: [PATCH 19/91] build(deps): bump ls-lint/action from 2.3.0 to 2.3.1 Bumps [ls-lint/action](https://github.com/ls-lint/action) from 2.3.0 to 2.3.1. - [Commits](https://github.com/ls-lint/action/compare/acc185de566968dd9b3ffc1b79dfc86e22306442...02e380fe8733d499cbfc9e22276de5085508a5bd) --- updated-dependencies: - dependency-name: ls-lint/action dependency-version: 2.3.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ee1808fa3a..3997ba84b82 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,7 @@ jobs: sudo apt-get update sudo apt-get install -y shellcheck - name: Run file and directory name linter - uses: ls-lint/action@acc185de566968dd9b3ffc1b79dfc86e22306442 # v2.3.0 + uses: ls-lint/action@02e380fe8733d499cbfc9e22276de5085508a5bd # v2.3.1 - name: Run shellcheck run: find . -name '*.sh' | xargs shellcheck - name: Install shfmt From 0ca23ccb34baf08f3cc32d56f3dc6f3eab7c7e83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 01:22:41 +0000 Subject: [PATCH 20/91] build(deps): bump the golang-x group with 3 updates Bumps the golang-x group with 3 updates: [golang.org/x/net](https://github.com/golang/net), [golang.org/x/sync](https://github.com/golang/sync) and [golang.org/x/text](https://github.com/golang/text). Updates `golang.org/x/net` from 0.40.0 to 0.41.0 - [Commits](https://github.com/golang/net/compare/v0.40.0...v0.41.0) Updates `golang.org/x/sync` from 0.14.0 to 0.15.0 - [Commits](https://github.com/golang/sync/compare/v0.14.0...v0.15.0) Updates `golang.org/x/text` from 0.25.0 to 0.26.0 - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.41.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/sync dependency-version: 0.15.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/text dependency-version: 0.26.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x ... Signed-off-by: dependabot[bot] --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 391502a9020..29af50c3d8d 100644 --- a/go.mod +++ b/go.mod @@ -41,10 +41,10 @@ require ( github.com/spf13/cobra v1.9.1 // gomodjail:unconfined github.com/spf13/pflag v1.0.6 github.com/wk8/go-ordered-map/v2 v2.1.8 - golang.org/x/net v0.40.0 - golang.org/x/sync v0.14.0 + golang.org/x/net v0.41.0 + golang.org/x/sync v0.15.0 golang.org/x/sys v0.33.0 // gomodjail:unconfined - golang.org/x/text v0.25.0 + golang.org/x/text v0.26.0 google.golang.org/grpc v1.72.2 google.golang.org/protobuf v1.36.6 // gomodjail:unconfined gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 @@ -115,12 +115,12 @@ require ( github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/mod v0.25.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/tools v0.33.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 1a513096797..18dc97f3c1d 100644 --- a/go.sum +++ b/go.sum @@ -290,8 +290,8 @@ golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -303,8 +303,8 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -319,8 +319,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -335,8 +335,8 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -385,8 +385,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -400,8 +400,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 7ddce1fcbc083df0246c3137857bfae15151f74f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 04:40:19 +0000 Subject: [PATCH 21/91] build(deps): bump google.golang.org/grpc from 1.72.2 to 1.73.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.2 to 1.73.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.72.2...v1.73.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.73.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 29af50c3d8d..b9af0fa2893 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( golang.org/x/sync v0.15.0 golang.org/x/sys v0.33.0 // gomodjail:unconfined golang.org/x/text v0.26.0 - google.golang.org/grpc v1.72.2 + google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 // gomodjail:unconfined gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gotest.tools/v3 v3.5.2 @@ -117,11 +117,11 @@ require ( github.com/yuin/gopher-lua v1.1.1 // indirect golang.org/x/crypto v0.39.0 // indirect golang.org/x/mod v0.25.0 // indirect - golang.org/x/oauth2 v0.26.0 // indirect + golang.org/x/oauth2 v0.28.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 18dc97f3c1d..2163c6b378a 100644 --- a/go.sum +++ b/go.sum @@ -270,16 +270,16 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -321,8 +321,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -406,10 +406,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= -google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= -google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 437b23db22271baef0175556ef06c41a3c28aec2 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Fri, 6 Jun 2025 12:49:52 +0300 Subject: [PATCH 22/91] chore: Do not install already installed jq and curl Signed-off-by: Oleksandr Redko --- .github/workflows/test.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3997ba84b82..febac6a2605 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -232,9 +232,7 @@ jobs: # QEMU: required by Lima itself # bash: required by test-templates.sh (OS version of bash is too old) # coreutils: required by test-templates.sh for the "timeout" command - # curl: required by test-templates.sh to download nerdctl for alpine - # jq: required by test-templates.sh to determine download URL for nerdctl - run: brew install qemu bash coreutils curl jq + run: brew install qemu bash coreutils - name: "Adjust LIMACTL_CREATE_ARGS" run: echo "LIMACTL_CREATE_ARGS=${LIMACTL_CREATE_ARGS} --vm-type=qemu" >>$GITHUB_ENV - name: "Inject `no_timer_check` to kernel cmdline" @@ -497,7 +495,7 @@ jobs: with: template: templates/${{ matrix.template }} - name: Install test dependencies - run: brew install bash coreutils jq + run: brew install bash coreutils - name: Uninstall qemu run: brew uninstall --ignore-dependencies --force qemu - name: Test From 973780e57d75ca01bac99f996c8ef67f5165ee92 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Fri, 6 Jun 2025 13:03:27 +0300 Subject: [PATCH 23/91] chore: Fix lint warnings in default.yaml Signed-off-by: Oleksandr Redko --- templates/default.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/default.yaml b/templates/default.yaml index 3b86d2d6985..99f01eccdf0 100644 --- a/templates/default.yaml +++ b/templates/default.yaml @@ -40,7 +40,7 @@ disk: null # 🟢 Builtin default: [] (Mount nothing) # 🔵 This file: Mount the home as read-only, /tmp/lima as writable (inherited via the `base` mechanism later in this file) mounts: [] -#- location: "~" +# - location: "~" # # Configure the mountPoint inside the guest. # # 🟢 Builtin default: value of location # mountPoint: null @@ -79,7 +79,7 @@ mounts: [] # # See https://www.kernel.org/doc/Documentation/filesystems/9p.txt # # 🟢 Builtin default: "fscache" for non-writable mounts, "mmap" for writable mounts # cache: null -#- location: "/tmp/lima" +# - location: "/tmp/lima" # # 🟢 Builtin default: false # # 🔵 This file: true (only for "/tmp/lima") # writable: true From a774d033b3ad6375a9a6f7fca39e2a5077791ae3 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 6 Jun 2025 17:05:43 -0700 Subject: [PATCH 24/91] docs: fix a small typo in the link to the vmnet page Signed-off-by: Tom McLaughlin --- website/content/en/docs/config/network/user.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/en/docs/config/network/user.md b/website/content/en/docs/config/network/user.md index 24d8859c0ce..87072058f7e 100644 --- a/website/content/en/docs/config/network/user.md +++ b/website/content/en/docs/config/network/user.md @@ -15,7 +15,7 @@ The guest IP address is set to `192.168.5.15`. This IP address is not accessible from the host by design. -Use [VMNet]]({{< ref "/docs/config/network/vmnet" >}}) to allow accessing the guest IP from the host and other guests. +Use [VMNet]({{< ref "/docs/config/network/vmnet" >}}) to allow accessing the guest IP from the host and other guests. ## Host IP (192.168.5.2) From d2d7756ec9fbb66cb0390ea63d360734d8bf4902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:03:40 +0000 Subject: [PATCH 25/91] build(deps): bump github.com/Code-Hex/vz/v3 from 3.6.0 to 3.7.0 Bumps [github.com/Code-Hex/vz/v3](https://github.com/Code-Hex/vz) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/Code-Hex/vz/releases) - [Commits](https://github.com/Code-Hex/vz/compare/v3.6.0...v3.7.0) --- updated-dependencies: - dependency-name: github.com/Code-Hex/vz/v3 dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b9af0fa2893..8134023f82c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ go 1.23.0 require ( al.essio.dev/pkg/shellescape v1.6.0 github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/Code-Hex/vz/v3 v3.6.0 // gomodjail:unconfined + github.com/Code-Hex/vz/v3 v3.7.0 // gomodjail:unconfined github.com/Microsoft/go-winio v0.6.2 // gomodjail:unconfined github.com/apparentlymart/go-cidr v1.1.0 github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e diff --git a/go.sum b/go.sum index 2163c6b378a..152136a8268 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/Code-Hex/go-infinity-channel v1.0.0 h1:M8BWlfDOxq9or9yvF9+YkceoTkDI1pFAqvnP87Zh0Nw= github.com/Code-Hex/go-infinity-channel v1.0.0/go.mod h1:5yUVg/Fqao9dAjcpzoQ33WwfdMWmISOrQloDRn3bsvY= -github.com/Code-Hex/vz/v3 v3.6.0 h1:S79dokzXmaLgC2yR0l0drRTGO/iFL3xwiCNVF80lJ5k= -github.com/Code-Hex/vz/v3 v3.6.0/go.mod h1:1LsW0jqW0r0cQ+IeR4hHbjdqOtSidNCVMWhStMHGho8= +github.com/Code-Hex/vz/v3 v3.7.0 h1:VEkfq5TVKnv85M81gQVPzLH9JzHrUJN/QQMpDZ+odPA= +github.com/Code-Hex/vz/v3 v3.7.0/go.mod h1:1LsW0jqW0r0cQ+IeR4hHbjdqOtSidNCVMWhStMHGho8= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= From 0933db8d1f0e276293a55ac2daa078cbce1d2aa1 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Wed, 11 Jun 2025 13:05:39 -0700 Subject: [PATCH 26/91] Add badge to DeepWiki AI docs Having the badge in the README will trigger the docs to be automatically rebuild once a week. Signed-off-by: Jan Dubois --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 81b3cc56fb6..8440c75c73e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ # Lima: Linux Machines +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/lima-vm/lima) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/6505/badge)](https://www.bestpractices.dev/projects/6505) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lima-vm/lima/badge)](https://scorecard.dev/viewer/?uri=github.com/lima-vm/lima) From e952923a782ec93726abf24880d0b8db79494eb4 Mon Sep 17 00:00:00 2001 From: "re:fi.64" Date: Sun, 1 Jun 2025 20:00:31 -0500 Subject: [PATCH 27/91] Search for the data directory using argv[0] *and* os.Executable() Some version managers like aqua rely on shims in $PATH to run the proper application. This isn't particularly unusual, but it was broken by the changes in #3566, because directory lookup ends up pointing to *the shim* rather than the underlying installation. The existing logic already has the concept of falling back to os.Executable if a path based on `argv[0]` can't be determined, so this issue can be solved by just always checking os.Executable *in addition* to `argv[0]`. Signed-off-by: re:fi.64 --- pkg/usrlocalsharelima/usrlocalsharelima.go | 98 ++++++++++++---------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go index 51fd4a1970a..0c8e99c7f84 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocalsharelima/usrlocalsharelima.go @@ -23,32 +23,42 @@ import ( // It will also append the file extension on Windows, if necessary. // This function is different from os.Executable(), which will use /proc/self/exe on Linux // and therefore will resolve any symlink used to locate the executable. This function will -// return the symlink instead because we want to locate ../share/lima relative to the location -// of the symlink, and not the actual executable. This is important when using Homebrew. -// -// If os.Args[0] is invalid, this function still falls back on os.Executable(). +// return the symlink instead because we want to be able to locate ../share/lima relative +// to the location of the symlink, and not the actual executable. This is important when +// using Homebrew. var executableViaArgs0 = sync.OnceValues(func() (string, error) { if os.Args[0] == "" { - logrus.Warn("os.Args[0] has not been set") - } else { - executable, err := exec.LookPath(os.Args[0]) - if err == nil { - // LookPath() will add the `.exe` file extension on Windows, but will not return an - // absolute path if the argument contained any of `:/\` (or just `/` on Unix). - return filepath.Abs(executable) - } - logrus.Warnf("os.Args[0] is invalid: %v", err) + return "", errors.New("os.Args[0] has not been set") + } + executable, err := exec.LookPath(os.Args[0]) + if err == nil { + executable, err = filepath.Abs(executable) + } + if err != nil { + return "", fmt.Errorf("os.Args[0] is invalid: %w", err) } - return os.Executable() + + return executable, nil }) // Dir returns the location of the /lima/share directory, relative to the location // of the current executable. It checks for multiple possible filesystem layouts and returns // the first candidate that contains the native guest agent binary. func Dir() (string, error) { - self, err := executableViaArgs0() + selfPaths := []string{} + + selfViaArgs0, err := executableViaArgs0() if err != nil { - return "", err + logrus.WithError(err).Warn("failed to find executable from os.Args[0]") + } else { + selfPaths = append(selfPaths, selfViaArgs0) + } + + selfViaOS, err := os.Executable() + if err != nil { + logrus.WithError(err).Warn("failed to find os.Executable()") + } else if len(selfPaths) == 0 || selfViaOS != selfPaths[0] { + selfPaths = append(selfPaths, selfViaOS) } ostype := limayaml.NewOS("linux") @@ -57,31 +67,35 @@ func Dir() (string, error) { return "", fmt.Errorf("failed to get arch for %q", runtime.GOARCH) } - // self: /usr/local/bin/limactl - selfDir := filepath.Dir(self) - selfDirDir := filepath.Dir(selfDir) - gaCandidates := []string{ - // candidate 0: - // - self: /Applications/Lima.app/Contents/MacOS/limactl - // - agent: /Applications/Lima.app/Contents/MacOS/lima-guestagent.Linux-x86_64 - // - dir: /Applications/Lima.app/Contents/MacOS - filepath.Join(selfDir, "lima-guestagent."+ostype+"-"+arch), - // candidate 1: - // - self: /usr/local/bin/limactl - // - agent: /usr/local/share/lima/lima-guestagent.Linux-x86_64 - // - dir: /usr/local/share/lima - filepath.Join(selfDirDir, "share/lima/lima-guestagent."+ostype+"-"+arch), - // TODO: support custom path - } - if debugutil.Debug { - // candidate 2: launched by `~/go/bin/dlv dap` - // - self: ${workspaceFolder}/cmd/limactl/__debug_bin_XXXXXX - // - agent: ${workspaceFolder}/_output/share/lima/lima-guestagent.Linux-x86_64 - // - dir: ${workspaceFolder}/_output/share/lima - candidateForDebugBuild := filepath.Join(filepath.Dir(selfDirDir), "_output/share/lima/lima-guestagent."+ostype+"-"+arch) - gaCandidates = append(gaCandidates, candidateForDebugBuild) - logrus.Infof("debug mode detected, adding more guest agent candidates: %v", candidateForDebugBuild) + gaCandidates := []string{} + for _, self := range selfPaths { + // self: /usr/local/bin/limactl + selfDir := filepath.Dir(self) + selfDirDir := filepath.Dir(selfDir) + gaCandidates = append(gaCandidates, + // candidate 0: + // - self: /Applications/Lima.app/Contents/MacOS/limactl + // - agent: /Applications/Lima.app/Contents/MacOS/lima-guestagent.Linux-x86_64 + // - dir: /Applications/Lima.app/Contents/MacOS + filepath.Join(selfDir, "lima-guestagent."+ostype+"-"+arch), + // candidate 1: + // - self: /usr/local/bin/limactl + // - agent: /usr/local/share/lima/lima-guestagent.Linux-x86_64 + // - dir: /usr/local/share/lima + filepath.Join(selfDirDir, "share/lima/lima-guestagent."+ostype+"-"+arch), + // TODO: support custom path + ) + if debugutil.Debug { + // candidate 2: launched by `~/go/bin/dlv dap` + // - self: ${workspaceFolder}/cmd/limactl/__debug_bin_XXXXXX + // - agent: ${workspaceFolder}/_output/share/lima/lima-guestagent.Linux-x86_64 + // - dir: ${workspaceFolder}/_output/share/lima + candidateForDebugBuild := filepath.Join(filepath.Dir(selfDirDir), "_output/share/lima/lima-guestagent."+ostype+"-"+arch) + gaCandidates = append(gaCandidates, candidateForDebugBuild) + logrus.Infof("debug mode detected, adding more guest agent candidates: %v", candidateForDebugBuild) + } } + for _, gaCandidate := range gaCandidates { if _, err := os.Stat(gaCandidate); err == nil { return filepath.Dir(gaCandidate), nil @@ -95,8 +109,8 @@ func Dir() (string, error) { } } - return "", fmt.Errorf("failed to find \"lima-guestagent.%s-%s\" binary for %q, attempted %v", - ostype, arch, self, gaCandidates) + return "", fmt.Errorf("failed to find \"lima-guestagent.%s-%s\" binary for %v, attempted %v", + ostype, arch, selfPaths, gaCandidates) } // GuestAgentBinary returns the absolute path of the guest agent binary, possibly with ".gz" suffix. From efc778e58c0ff3b6147742a6a5b03358344369e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 01:28:02 +0000 Subject: [PATCH 28/91] build(deps): bump github/codeql-action from 3.28.19 to 3.29.0 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.19 to 3.29.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/fca7ace96b7d713c7035871441bd52efbe39e27e...ce28f5bb42b7a9f2c824e633a3f6ee835bab6858) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yaml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 6cd1ef292fe..15d1155f7ac 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Initialize CodeQL - uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 + uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -48,6 +48,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 + uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 06056b9f38f..49f7a95ea3f 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 + uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 with: sarif_file: results.sarif From d421eee7f02b7f397467ee6ead9bf24142c3824a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 01:28:07 +0000 Subject: [PATCH 29/91] build(deps): bump actions/attest-build-provenance from 2.3.0 to 2.4.0 Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/db473fddc028af60658334401dc6fa3ffd8669fd...e8998f949152b193b063cb0ec769d69d929409be) --- updated-dependencies: - dependency-name: actions/attest-build-provenance dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f571f7cb720..0cf4435da3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,7 +109,7 @@ jobs: - - - Release manager: [ADD YOUR NAME HERE] (@[ADD YOUR GITHUB ID HERE]) EOF - - uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 + - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0 if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') with: subject-path: _artifacts/* From 5a0b5aa77a5776c2a3d1eb10c3ab4a8708b7458e Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Sun, 15 Jun 2025 15:32:25 +0300 Subject: [PATCH 30/91] limactl create: do not show "hidden" templates Signed-off-by: Oleksandr Redko --- cmd/limactl/start.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index ad0534847bc..3f90f66ee4b 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -338,7 +338,7 @@ func chooseNextCreatorState(tmpl *limatmpl.Template, yq string) (*limatmpl.Templ } return tmpl, nil case 2: // "Choose another template..." - templates, err := templatestore.Templates() + templates, err := filterHiddenTemplates() if err != nil { return tmpl, err } @@ -379,22 +379,34 @@ func createStartActionCommon(cmd *cobra.Command, _ []string) (exit bool, err err if listTemplates, err := cmd.Flags().GetBool("list-templates"); err != nil { return true, err } else if listTemplates { - templates, err := templatestore.Templates() + templates, err := filterHiddenTemplates() if err != nil { return true, err } w := cmd.OutOrStdout() for _, f := range templates { - // Don't show internal base templates like `_default/*` and `_images/*`. - if !strings.HasPrefix(f.Name, "_") { - _, _ = fmt.Fprintln(w, f.Name) - } + _, _ = fmt.Fprintln(w, f.Name) } return true, nil } return false, nil } +func filterHiddenTemplates() ([]templatestore.Template, error) { + templates, err := templatestore.Templates() + if err != nil { + return nil, err + } + var filtered []templatestore.Template + for _, f := range templates { + // Don't show internal base templates like `_default/*` and `_images/*`. + if !strings.HasPrefix(f.Name, "_") { + filtered = append(filtered, f) + } + } + return filtered, nil +} + func createAction(cmd *cobra.Command, args []string) error { if exit, err := createStartActionCommon(cmd, args); err != nil { return err From d46da58b8dd5b2007df1ad7c145d3c492edbf688 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Mon, 16 Jun 2025 23:28:38 +0300 Subject: [PATCH 31/91] fix(edk2): update candidates path on Arch Linux Signed-off-by: fruzitent --- pkg/qemu/qemu.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go index ca775b2d7bc..0ba01b616d5 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/qemu/qemu.go @@ -1146,6 +1146,9 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) { switch arch { case limayaml.X8664: + // Archlinux package "edk2-ovmf" + // @see: https://archlinux.org/packages/extra/any/edk2-ovmf/files + candidates = append(candidates, "/usr/share/edk2/x64/OVMF_CODE.4m.fd") // Debian package "ovmf" candidates = append(candidates, "/usr/share/OVMF/OVMF_CODE.fd") candidates = append(candidates, "/usr/share/OVMF/OVMF_CODE_4M.fd") @@ -1153,15 +1156,19 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) { candidates = append(candidates, "/usr/share/edk2/ovmf/OVMF_CODE.fd") // openSUSE package "qemu-ovmf-x86_64" candidates = append(candidates, "/usr/share/qemu/ovmf-x86_64.bin") - // Archlinux package "edk2-ovmf" - candidates = append(candidates, "/usr/share/edk2-ovmf/x64/OVMF_CODE.fd") case limayaml.AARCH64: + // Archlinux package "edk2-aarch64" + // @see: https://archlinux.org/packages/extra/any/edk2-aarch64/files + candidates = append(candidates, "/usr/share/edk2/aarch64/QEMU_CODE.fd") // Debian package "qemu-efi-aarch64" // Fedora package "edk2-aarch64" candidates = append(candidates, "/usr/share/AAVMF/AAVMF_CODE.fd") // Debian package "qemu-efi-aarch64" (unpadded, backwards compatibility) candidates = append(candidates, "/usr/share/qemu-efi-aarch64/QEMU_EFI.fd") case limayaml.ARMV7L: + // Archlinux package "edk2-arm" + // @see: https://archlinux.org/packages/extra/any/edk2-arm/files + candidates = append(candidates, "/usr/share/edk2/arm/QEMU_CODE.fd") // Debian package "qemu-efi-arm" // Fedora package "edk2-arm" candidates = append(candidates, "/usr/share/AAVMF/AAVMF32_CODE.fd") From 04a6ef517d1e19ee0f04eec733e60c535b91c8a2 Mon Sep 17 00:00:00 2001 From: Kenichi Kamiya Date: Tue, 17 Jun 2025 07:42:58 +0900 Subject: [PATCH 32/91] website: update link to Nix file File path was moved in: https://github.com/NixOS/nixpkgs/commit/f89d3cfe2f0d6a82752643375ce8572f363ab0a4 Signed-off-by: Kenichi Kamiya --- website/content/en/docs/installation/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/en/docs/installation/_index.md b/website/content/en/docs/installation/_index.md index 25190610f40..40834393282 100644 --- a/website/content/en/docs/installation/_index.md +++ b/website/content/en/docs/installation/_index.md @@ -36,7 +36,7 @@ Port: nix-env -i lima ``` -Nix file: +Nix file: {{% /tab %}} {{% tab header="Binary" %}} From 820fba68bf30cce80ce114cd28655cc4e4ca34a4 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Tue, 17 Jun 2025 12:43:36 +0300 Subject: [PATCH 33/91] docs: update links to policies Signed-off-by: Oleksandr Redko --- README.md | 2 +- website/content/en/docs/community/contributing.md | 4 ++-- website/content/en/docs/community/governance.md | 2 +- website/content/en/docs/config/multi-arch.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 81b3cc56fb6..c7faa9629fd 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ GUI: - Login: ### Code of Conduct -Lima follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). +Lima follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). - - - **We are a [Cloud Native Computing Foundation](https://cncf.io/) sandbox project.** diff --git a/website/content/en/docs/community/contributing.md b/website/content/en/docs/community/contributing.md index c2504ddf7ee..641c597ac37 100644 --- a/website/content/en/docs/community/contributing.md +++ b/website/content/en/docs/community/contributing.md @@ -9,13 +9,13 @@ Every commit must be signed off with the `Signed-off-by: REAL NAME . +See also . ## Licensing Lima is licensed under the terms of [Apache License, Version 2.0](https://github.com/lima-vm/lima/blob/master/LICENSE). -See also for third-party dependencies. +See also for third-party dependencies. ## Sending pull requests diff --git a/website/content/en/docs/community/governance.md b/website/content/en/docs/community/governance.md index d88cab74274..490bf80576f 100644 --- a/website/content/en/docs/community/governance.md +++ b/website/content/en/docs/community/governance.md @@ -6,7 +6,7 @@ weight: 10 ## Code of Conduct -Lima follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). +Lima follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). ## Maintainership Lima is governed by Maintainers who are elected from active contributors. diff --git a/website/content/en/docs/config/multi-arch.md b/website/content/en/docs/config/multi-arch.md index aa279c0dd98..ee77f07228f 100644 --- a/website/content/en/docs/config/multi-arch.md +++ b/website/content/en/docs/config/multi-arch.md @@ -58,7 +58,7 @@ $ lima nerdctl build --platform=amd64,arm64 -t example.com/foo:latest . $ lima nerdctl push --all-platforms example.com/foo:latest ``` -See also https://github.com/containerd/nerdctl/blob/master/docs/multi-platform.md +See also https://github.com/containerd/nerdctl/blob/main/docs/multi-platform.md ## [Fast mode 2 (Rosetta): Intel containers on ARM VM on ARM Host](#fast-mode-2) From 9f7b9a884e3f6ffe8bbc0228f0b564ec736495e6 Mon Sep 17 00:00:00 2001 From: Arthur Sengileyev Date: Sat, 26 Apr 2025 19:36:01 +0300 Subject: [PATCH 34/91] Add QEMU on Windows to CI Signed-off-by: Arthur Sengileyev --- .github/workflows/test.yml | 38 ++++++++++++++++++++++++++++++++++++-- hack/test-templates.sh | 5 +++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index febac6a2605..071c9984df5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -151,8 +151,8 @@ jobs: run: sudo make uninstall windows: - name: "Windows tests" - runs-on: windows-2022-8-cores + name: "Windows tests (WSL2)" + runs-on: windows-2025-8-cores timeout-minutes: 30 steps: - name: Enable WSL2 @@ -205,6 +205,40 @@ jobs: $env:_LIMA_WINDOWS_EXTRA_PATH = 'C:\Program Files\Git\usr\bin' bash.exe -c "./hack/test-templates.sh templates/experimental/wsl2.yaml" + windows-qemu: + name: "Windows tests (QEMU)" + runs-on: windows-2025 + timeout-minutes: 30 + steps: + - name: Set gitconfig + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + # To avoid "can't parse builtin Lima version" errors + fetch-depth: 0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 + with: + go-version: 1.24.x + - name: Unit tests + run: go test -v ./... + - name: Make + run: make + - name: Install QEMU + run: | + winget install --silent --accept-source-agreements --accept-package-agreements --disable-interactivity SoftwareFreedomConservancy.QEMU + - name: Integration tests (QEMU, Windows host) + run: | + $env:PATH = "$pwd\_output\bin;" + 'C:\msys64\usr\bin;' + 'C:\Program Files\QEMU;' + $env:PATH + pacman -Sy --noconfirm openbsd-netcat diffutils + $env:MSYS2_ENV_CONV_EXCL = 'HOME_HOST;HOME_GUEST;_LIMA_WINDOWS_EXTRA_PATH' + $env:HOME_HOST = $(cygpath.exe "$env:USERPROFILE") + $env:HOME_GUEST = "$env:HOME_HOST" + $env:LIMACTL_CREATE_ARGS = '--vm-type=qemu' + $env:_LIMA_WINDOWS_EXTRA_PATH = 'C:\Program Files\Git\usr\bin' + bash.exe -c "./hack/test-templates.sh templates/default.yaml" + qemu: name: "Integration tests (QEMU, macOS host)" runs-on: macos-15-large # Intel diff --git a/hack/test-templates.sh b/hack/test-templates.sh index cd228a262eb..9bba8ba3785 100755 --- a/hack/test-templates.sh +++ b/hack/test-templates.sh @@ -60,6 +60,11 @@ declare -A CHECKS=( ) case "$NAME" in +"default") + # CI failure: + # "[hostagent] failed to confirm whether /c/Users/runneradmin [remote] is successfully mounted" + [ "${OS_HOST}" = "Msys" ] && CHECKS["mount-home"]= + ;; "alpine"*) WARNING "Alpine does not support systemd" CHECKS["systemd"]= From 28777816f74038b42f6a4b6cc2c2a7b3a865ddba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:37:48 +0000 Subject: [PATCH 35/91] build(deps): bump actions/setup-go from 5.4.0 to 5.5.0 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5.4.0...d35c59abb061a4a6fb18e82ac0862c26744d6ab5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 5.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 071c9984df5..c3670fd353e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -218,7 +218,7 @@ jobs: with: # To avoid "can't parse builtin Lima version" errors fetch-depth: 0 - - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: 1.24.x - name: Unit tests From a296ca3259e0e90917ef7787e8eac23bebde5205 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 19 Jun 2025 19:18:50 +0300 Subject: [PATCH 36/91] fix: update provision mode validation error message Signed-off-by: Oleksandr Redko --- pkg/limayaml/validate.go | 2 +- pkg/limayaml/validate_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 750383e2c51..8298882ce81 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -219,7 +219,7 @@ func Validate(y *LimaYAML, warn bool) error { case ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible: default: return fmt.Errorf("field `provision[%d].mode` must one of %q, %q, %q, %q, %q, or %q", - i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeDependency, ProvisionModeAnsible, ProvisionModeAnsible) + i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible) } if p.Mode != ProvisionModeDependency && p.SkipDefaultDependencyResolution != nil { return fmt.Errorf("field `provision[%d].mode` cannot set skipDefaultDependencyResolution, only valid on scripts of type %q", diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index ed2233822b5..d23db80261b 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -40,6 +40,37 @@ func TestValidateProbes(t *testing.T) { assert.Error(t, err, "field `probe[0].file.digest` support is not yet implemented") } +func TestValidateProvisionMode(t *testing.T) { + images := `images: [{location: /}]` + provisionBoot := `provision: [{mode: boot, script: "touch /tmp/param-$PARAM_BOOT"}]` + y, err := Load([]byte(provisionBoot+"\n"+images), "lima.yaml") + assert.NilError(t, err) + + err = Validate(y, false) + assert.NilError(t, err) + + provisionUser := `provision: [{mode: user, script: "touch /tmp/param-$PARAM_USER"}]` + y, err = Load([]byte(provisionUser+"\n"+images), "lima.yaml") + assert.NilError(t, err) + + err = Validate(y, false) + assert.NilError(t, err) + + provisionDependency := `provision: [{mode: ansible, script: "touch /tmp/param-$PARAM_DEPENDENCY"}]` + y, err = Load([]byte(provisionDependency+"\n"+images), "lima.yaml") + assert.NilError(t, err) + + err = Validate(y, false) + assert.NilError(t, err) + + provisionInvalid := `provision: [{mode: invalid}]` + y, err = Load([]byte(provisionInvalid+"\n"+images), "lima.yaml") + assert.NilError(t, err) + + err = Validate(y, false) + assert.Error(t, err, "field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"") +} + func TestValidateProvisionData(t *testing.T) { images := `images: [{location: /}]` validData := `provision: [{mode: data, path: /tmp, content: hello}]` From 8b0f86e0922f1bd09acfc0b38a43dab2f9050644 Mon Sep 17 00:00:00 2001 From: Kenichi Kamiya Date: Thu, 19 Jun 2025 02:38:47 +0900 Subject: [PATCH 37/91] sshutil: suppress warnings when SSH lacks GSSAPI Co-authored-by: Oleksandr Redko Signed-off-by: Kenichi Kamiya Signed-off-by: Oleksandr Redko --- pkg/sshutil/sshutil.go | 70 +++++++++++++++++++++++++------------ pkg/sshutil/sshutil_test.go | 10 ++++++ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go index 81e81e373fe..fa5370a422d 100644 --- a/pkg/sshutil/sshutil.go +++ b/pkg/sshutil/sshutil.go @@ -154,13 +154,22 @@ func DefaultPubKeys(loadDotSSH bool) ([]PubKey, error) { return res, nil } +type openSSHInfo struct { + // Version is set to the version of OpenSSH, or semver.New("0.0.0") if the version cannot be determined. + Version semver.Version + + // Some distributions omit this feature by default, for example, Alpine, NixOS. + GSSAPISupported bool +} + var sshInfo struct { sync.Once // aesAccelerated is set to true when AES acceleration is available. // Available on almost all modern Intel/AMD processors. aesAccelerated bool - // openSSHVersion is set to the version of OpenSSH, or semver.New("0.0.0") if the version cannot be determined. - openSSHVersion semver.Version + + // OpenSSH executable information for the version and supported options. + openSSH openSSHInfo } // CommonOpts returns ssh option key-value pairs like {"IdentityFile=/path/to/id_foo"}. @@ -226,7 +235,6 @@ func CommonOpts(sshPath string, useDotSSH bool) ([]string, error) { "StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null", "NoHostAuthenticationForLocalhost=yes", - "GSSAPIAuthentication=no", "PreferredAuthentications=publickey", "Compression=no", "BatchMode=yes", @@ -235,11 +243,15 @@ func CommonOpts(sshPath string, useDotSSH bool) ([]string, error) { sshInfo.Do(func() { sshInfo.aesAccelerated = detectAESAcceleration() - sshInfo.openSSHVersion = DetectOpenSSHVersion(sshPath) + sshInfo.openSSH = detectOpenSSHInfo(sshPath) }) + if sshInfo.openSSH.GSSAPISupported { + opts = append(opts, "GSSAPIAuthentication=no") + } + // Only OpenSSH version 8.1 and later support adding ciphers to the front of the default set - if !sshInfo.openSSHVersion.LessThan(*semver.New("8.1.0")) { + if !sshInfo.openSSH.Version.LessThan(*semver.New("8.1.0")) { // By default, `ssh` choose chacha20-poly1305@openssh.com, even when AES accelerator is available. // (OpenSSH_8.1p1, macOS 11.6, MacBookPro 2020, Core i7-1068NG7) // @@ -321,7 +333,7 @@ func SSHArgsFromOpts(opts []string) []string { } func ParseOpenSSHVersion(version []byte) *semver.Version { - regex := regexp.MustCompile(`^OpenSSH_(\d+\.\d+)(?:p(\d+))?\b`) + regex := regexp.MustCompile(`(?m)^OpenSSH_(\d+\.\d+)(?:p(\d+))?\b`) matches := regex.FindSubmatch(version) if len(matches) == 3 { if len(matches[2]) == 0 { @@ -332,6 +344,10 @@ func ParseOpenSSHVersion(version []byte) *semver.Version { return &semver.Version{} } +func parseOpenSSHGSSAPISupported(version string) bool { + return !strings.Contains(version, `Unsupported option "gssapiauthentication"`) +} + // sshExecutable beyond path also records size and mtime, in the case of ssh upgrades. type sshExecutable struct { Path string @@ -340,14 +356,14 @@ type sshExecutable struct { } var ( - // sshVersions caches the parsed version of each ssh executable, if it is needed again. - sshVersions = map[sshExecutable]*semver.Version{} - sshVersionsRW sync.RWMutex + // openSSHInfos caches the parsed version and supported options of each ssh executable, if it is needed again. + openSSHInfos = map[sshExecutable]*openSSHInfo{} + openSSHInfosRW sync.RWMutex ) -func DetectOpenSSHVersion(ssh string) semver.Version { +func detectOpenSSHInfo(ssh string) openSSHInfo { var ( - v semver.Version + info openSSHInfo exe sshExecutable stderr bytes.Buffer ) @@ -357,25 +373,33 @@ func DetectOpenSSHVersion(ssh string) semver.Version { } else { st, _ := os.Stat(path) exe = sshExecutable{Path: path, Size: st.Size(), ModTime: st.ModTime()} - sshVersionsRW.RLock() - ver := sshVersions[exe] - sshVersionsRW.RUnlock() - if ver != nil { - return *ver + openSSHInfosRW.RLock() + info := openSSHInfos[exe] + openSSHInfosRW.RUnlock() + if info != nil { + return *info } } - cmd := exec.Command(path, "-V") + // -V should be last + cmd := exec.Command(path, "-o", "GSSAPIAuthentication=no", "-V") cmd.Stderr = &stderr if err := cmd.Run(); err != nil { logrus.Warnf("failed to run %v: stderr=%q", cmd.Args, stderr.String()) } else { - v = *ParseOpenSSHVersion(stderr.Bytes()) - logrus.Debugf("OpenSSH version %s detected", v) - sshVersionsRW.Lock() - sshVersions[exe] = &v - sshVersionsRW.Unlock() + info = openSSHInfo{ + Version: *ParseOpenSSHVersion(stderr.Bytes()), + GSSAPISupported: parseOpenSSHGSSAPISupported(stderr.String()), + } + logrus.Debugf("OpenSSH version %s detected, is GSSAPI supported: %t", info.Version, info.GSSAPISupported) + openSSHInfosRW.Lock() + openSSHInfos[exe] = &info + openSSHInfosRW.Unlock() } - return v + return info +} + +func DetectOpenSSHVersion(ssh string) semver.Version { + return detectOpenSSHInfo(ssh).Version } // detectValidPublicKey returns whether content represent a public key. diff --git a/pkg/sshutil/sshutil_test.go b/pkg/sshutil/sshutil_test.go index 972c3037b1a..4b8b81cca80 100644 --- a/pkg/sshutil/sshutil_test.go +++ b/pkg/sshutil/sshutil_test.go @@ -29,6 +29,16 @@ func TestParseOpenSSHVersion(t *testing.T) { // OpenBSD 5.8 assert.Check(t, ParseOpenSSHVersion([]byte("OpenSSH_7.0, LibreSSL")).Equal(*semver.New("7.0.0"))) + + // NixOS 25.05 + assert.Check(t, ParseOpenSSHVersion([]byte(`command-line line 0: Unsupported option "gssapiauthentication" +OpenSSH_10.0p2, OpenSSL 3.4.1 11 Feb 2025`)).Equal(*semver.New("10.0.2"))) +} + +func TestParseOpenSSHGSSAPISupported(t *testing.T) { + assert.Check(t, parseOpenSSHGSSAPISupported("OpenSSH_8.4p1 Ubuntu")) + assert.Check(t, !parseOpenSSHGSSAPISupported(`command-line line 0: Unsupported option "gssapiauthentication" +OpenSSH_10.0p2, OpenSSL 3.4.1 11 Feb 2025`)) } func Test_detectValidPublicKey(t *testing.T) { From b6b1f3b761c01b70ca6386cbd0e805e797b4ec56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 01:57:57 +0000 Subject: [PATCH 38/91] build(deps): bump the k8s group with 3 updates Bumps the k8s group with 3 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.32.5 to 0.32.6 - [Commits](https://github.com/kubernetes/api/compare/v0.32.5...v0.32.6) Updates `k8s.io/apimachinery` from 0.32.5 to 0.32.6 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.32.5...v0.32.6) Updates `k8s.io/client-go` from 0.32.5 to 0.32.6 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.32.5...v0.32.6) --- updated-dependencies: - dependency-name: k8s.io/api dependency-version: 0.32.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s - dependency-name: k8s.io/apimachinery dependency-version: 0.32.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s - dependency-name: k8s.io/client-go dependency-version: 0.32.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8134023f82c..aef5c3df233 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,9 @@ require ( google.golang.org/protobuf v1.36.6 // gomodjail:unconfined gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gotest.tools/v3 v3.5.2 - k8s.io/api v0.32.5 - k8s.io/apimachinery v0.32.5 - k8s.io/client-go v0.32.5 + k8s.io/api v0.32.6 + k8s.io/apimachinery v0.32.6 + k8s.io/client-go v0.32.6 ) require ( diff --git a/go.sum b/go.sum index 152136a8268..b0cb3d470f4 100644 --- a/go.sum +++ b/go.sum @@ -430,12 +430,12 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f h1:O2w2DymsOlM/nv2pLNWCMCYOldgBBMkD7H0/prN5W2k= gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= -k8s.io/api v0.32.5 h1:uqjjsYo1kTJr5NIcoIaP9F+TgXgADH7nKQx91FDAhtk= -k8s.io/api v0.32.5/go.mod h1:bXXFU3fGCZ/eFMZvfHZC69PeGbXEL4zzjuPVzOxHF64= -k8s.io/apimachinery v0.32.5 h1:6We3aJ6crC0ap8EhsEXcgX3LpI6SEjubpiOMXLROwPM= -k8s.io/apimachinery v0.32.5/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.5 h1:huFmQMzgWu0z4kbWsuZci+Gt4Fo72I4CcrvhToZ/Qp0= -k8s.io/client-go v0.32.5/go.mod h1:Qchw6f9WIVrur7DKojAHpRgGLcANT0RLIvF39Jz58xA= +k8s.io/api v0.32.6 h1:UiBAMRzTP24Tz9UT1uhhmAv1auGTT9PT/npywSk9JrU= +k8s.io/api v0.32.6/go.mod h1:+iFCyQN34v2rsL53iQEN9lYE03mFdgPvgSXvATIDteg= +k8s.io/apimachinery v0.32.6 h1:odtEUjg7OT3132sBFsFn4Arj4Gd+BplYekmLQP8L3ak= +k8s.io/apimachinery v0.32.6/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.6 h1:Q+O+Sd9LKKFnsGZNVX2q1RDILYRpQZX+ea2RoIgjKlM= +k8s.io/client-go v0.32.6/go.mod h1:yqL9XJ2cTXy3WdJwdeyob3O6xiLwWrh9DP7SeszniW0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= From 8f9d1013b041fabc874ac3ce46695a343728e31b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 01:58:06 +0000 Subject: [PATCH 39/91] build(deps): bump github.com/google/yamlfmt from 0.17.0 to 0.17.1 Bumps [github.com/google/yamlfmt](https://github.com/google/yamlfmt) from 0.17.0 to 0.17.1. - [Release notes](https://github.com/google/yamlfmt/releases) - [Changelog](https://github.com/google/yamlfmt/blob/main/.goreleaser.yaml) - [Commits](https://github.com/google/yamlfmt/compare/v0.17.0...v0.17.1) --- updated-dependencies: - dependency-name: github.com/google/yamlfmt dependency-version: 0.17.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8134023f82c..49d264aac95 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/foxcpp/go-mockdns v1.1.0 github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/google/yamlfmt v0.17.0 + github.com/google/yamlfmt v0.17.1 github.com/invopop/jsonschema v0.13.0 github.com/lima-vm/go-qcow2reader v0.6.0 github.com/lima-vm/sshocker v0.3.8 // gomodjail:unconfined diff --git a/go.sum b/go.sum index 152136a8268..4804db130ec 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/yamlfmt v0.17.0 h1:/tdp01rIlvLz3LgJ2NtMLnqgAadZm33P7GcPU680b+w= -github.com/google/yamlfmt v0.17.0/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= +github.com/google/yamlfmt v0.17.1 h1:NgKi21+MUVntxhPhIsstylN6QNCCfDv3wKNKwlm/3Hs= +github.com/google/yamlfmt v0.17.1/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= From 03907f69127d5f3d0838057cb8241620579d06a7 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 15:27:30 +0900 Subject: [PATCH 40/91] templates: add rocky-10 Signed-off-by: Akihiro Suda --- templates/README.md | 3 ++- templates/_images/rocky-10.yaml | 29 +++++++++++++++++++++++++++++ templates/rocky-10.yaml | 5 +++++ templates/rocky.yaml | 2 +- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 templates/_images/rocky-10.yaml create mode 100644 templates/rocky-10.yaml diff --git a/templates/README.md b/templates/README.md index da51eeedda5..ebb7998c705 100644 --- a/templates/README.md +++ b/templates/README.md @@ -28,7 +28,8 @@ Distro: - [`oraclelinux-8`](./oraclelinux-8.yaml): Oracle Linux 8 - [`oraclelinux-9`](./oraclelinux-9.yaml), `oraclelinux.yaml`: Oracle Linux 9 - [`rocky-8`](./rocky-8.yaml): Rocky Linux 8 -- [`rocky-9`](./rocky-9.yaml), `rocky.yaml`: Rocky Linux 9 +- [`rocky-9`](./rocky-9.yaml): Rocky Linux 9 +- [`rocky-10`](./rocky-10.yaml), `rocky.yaml`: Rocky Linux 10 - [`ubuntu`](./ubuntu.yaml): Ubuntu (same as `default.yaml` but without extra YAML lines) - [`ubuntu-lts`](./ubuntu-lts.yaml): Ubuntu LTS (same as `ubuntu.yaml` but pinned to an LTS version) - [`experimental/gentoo`](./experimental/gentoo.yaml): [experimental] Gentoo diff --git a/templates/_images/rocky-10.yaml b/templates/_images/rocky-10.yaml new file mode 100644 index 00000000000..f1523e11ee2 --- /dev/null +++ b/templates/_images/rocky-10.yaml @@ -0,0 +1,29 @@ +images: +- location: "https://dl.rockylinux.org/pub/rocky/10.0/images/x86_64/Rocky-10-GenericCloud-Base-10.0-20250609.1.x86_64.qcow2" + arch: "x86_64" + digest: "sha256:20e771c654724e002c32fb92a05fdfdd7ac878c192f50e2fc21f53e8f098b8f9" +- location: "https://dl.rockylinux.org/pub/rocky/10.0/images/aarch64/Rocky-10-GenericCloud-Base-10.0-20250609.1.aarch64.qcow2" + arch: "aarch64" + digest: "sha256:326264421955473a3576feff35076b7a7ef4bf2a14b5f6d238b7ec65c0426fbc" +- location: "https://dl.rockylinux.org/pub/rocky/10.0/images/ppc64le/Rocky-10-GenericCloud-Base-10.0-20250609.1.ppc64le.qcow2" + arch: "ppc64le" + digest: "sha256:aba0ecaf13afccc90e30388eb61d89071bac26818f06e815c6d764f5ccd9bef4" +- location: "https://dl.rockylinux.org/pub/rocky/10.0/images/s390x/Rocky-10-GenericCloud-Base-10.0-20250609.1.s390x.qcow2" + arch: "s390x" + digest: "sha256:ecaf7c23f64f4c229a851cd9e263d3b31b4a877e9a01a420d27d20e341c3e681" +# Fallback to the latest release image. +# Hint: run `limactl prune` to invalidate the cache + +- location: https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 + arch: x86_64 + +- location: https://dl.rockylinux.org/pub/rocky/10/images/aarch64/Rocky-10-GenericCloud-Base.latest.aarch64.qcow2 + arch: aarch64 + +- location: https://dl.rockylinux.org/pub/rocky/10/images/ppc64le/Rocky-10-GenericCloud-Base.latest.ppc64le.qcow2 + arch: ppc64le + +- location: https://dl.rockylinux.org/pub/rocky/10/images/s390x/Rocky-10-GenericCloud-Base.latest.s390x.qcow2 + arch: s390x + +mountTypesUnsupported: [9p] diff --git a/templates/rocky-10.yaml b/templates/rocky-10.yaml new file mode 100644 index 00000000000..01dd20082aa --- /dev/null +++ b/templates/rocky-10.yaml @@ -0,0 +1,5 @@ +minimumLimaVersion: 1.1.0 + +base: +- template://_images/rocky-10 +- template://_default/mounts diff --git a/templates/rocky.yaml b/templates/rocky.yaml index daf85002cf6..7730bdd795a 120000 --- a/templates/rocky.yaml +++ b/templates/rocky.yaml @@ -1 +1 @@ -rocky-9.yaml \ No newline at end of file +rocky-10.yaml \ No newline at end of file From 7801dfdf9f86ab7932acd3a93f8fcfa815d2e244 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 15:36:18 +0900 Subject: [PATCH 41/91] templates: rocky-9: bump up to 9.6 and add s390x Signed-off-by: Akihiro Suda --- templates/_images/rocky-9.yaml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/templates/_images/rocky-9.yaml b/templates/_images/rocky-9.yaml index 82ed7ed264a..d1dce731c28 100644 --- a/templates/_images/rocky-9.yaml +++ b/templates/_images/rocky-9.yaml @@ -1,16 +1,16 @@ images: -- location: https://dl.rockylinux.org/pub/rocky/9.5/images/x86_64/Rocky-9-GenericCloud-Base-9.5-20241118.0.x86_64.qcow2 - arch: x86_64 - digest: sha256:069493fdc807300a22176540e9171fcff2227a92b40a7985a0c1c9e21aeebf57 - -- location: https://dl.rockylinux.org/pub/rocky/9.5/images/aarch64/Rocky-9-GenericCloud-Base-9.5-20241118.0.aarch64.qcow2 - arch: aarch64 - digest: sha256:5443bcc0507fadc3d7bd3e8d266135ab8db6966c703216933f824164fd3252f1 - -- location: https://dl.rockylinux.org/pub/rocky/9.5/images/ppc64le/Rocky-9-GenericCloud-Base-9.5-20241118.0.ppc64le.qcow2 - arch: ppc64le - digest: sha256:814a5b80bacb370baa705766de40c4c96e44cc7fa03fcb6f41c8dfbc89aa971a - +- location: "https://dl.rockylinux.org/pub/rocky/9.6/images/x86_64/Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2" + arch: "x86_64" + digest: "sha256:2c72815bb83cadccbede4704780e9b52033722db8a45c3fb02130aa380690a3d" +- location: "https://dl.rockylinux.org/pub/rocky/9.6/images/aarch64/Rocky-9-GenericCloud-Base-9.6-20250531.0.aarch64.qcow2" + arch: "aarch64" + digest: "sha256:3776b2c17cc011c28e2ab440c49dfba8d2be214d8b85df2b5edc97ebdeb30e4a" +- location: "https://dl.rockylinux.org/pub/rocky/9.6/images/ppc64le/Rocky-9-GenericCloud-Base-9.6-20250531.0.ppc64le.qcow2" + arch: "ppc64le" + digest: "sha256:03dcfa25cef7b372506b34a5c30892b1d88ad48bc2670493519ab03088febedd" +- location: "https://dl.rockylinux.org/pub/rocky/9.6/images/s390x/Rocky-9-GenericCloud-Base-9.6-20250531.0.s390x.qcow2" + arch: "s390x" + digest: "sha256:f4aa994c02fd831c0f83f4f2f7d95e410a1ca3df3902472059e6fbe8fab883fa" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache @@ -23,4 +23,7 @@ images: - location: https://dl.rockylinux.org/pub/rocky/9/images/ppc64le/Rocky-9-GenericCloud.latest.ppc64le.qcow2 arch: ppc64le +- location: https://dl.rockylinux.org/pub/rocky/9/images/s390x/Rocky-9-GenericCloud.latest.s390x.qcow2 + arch: s390x + mountTypesUnsupported: [9p] From f34e792d10d1047bd3c440a9b8f868ff3cc2214f Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 15:40:48 +0900 Subject: [PATCH 42/91] templates: almalinux-10: add ppc64le Signed-off-by: Akihiro Suda --- templates/_images/almalinux-10.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/templates/_images/almalinux-10.yaml b/templates/_images/almalinux-10.yaml index 6f49d8a9ec8..e138e9e9e83 100644 --- a/templates/_images/almalinux-10.yaml +++ b/templates/_images/almalinux-10.yaml @@ -8,6 +8,10 @@ images: - location: "https://repo.almalinux.org/almalinux/10.0/cloud/s390x/images/AlmaLinux-10-GenericCloud-10.0-20250528.0.s390x.qcow2" arch: "s390x" digest: "sha256:a867b0ee9b9e537aa2d599430ee9be0808fc66f723dbf5937b204bfeb449b207" +- location: "https://repo.almalinux.org/almalinux/10.0/cloud/ppc64le/images/AlmaLinux-10-GenericCloud-10.0-20250616.0.ppc64le.qcow2" + arch: "ppc64le" + digest: "sha256:7e45bd615ee557777b2b6b72cceada429e96c61ddcc299bf587ef5edf4a44757" + # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache @@ -20,4 +24,7 @@ images: - location: https://repo.almalinux.org/almalinux/10/cloud/s390x/images/AlmaLinux-10-GenericCloud-latest.s390x.qcow2 arch: s390x +- location: https://repo.almalinux.org/almalinux/10/cloud/ppc64le/images/AlmaLinux-10-GenericCloud-latest.ppc64le.qcow2 + arch: ppc64le + mountTypesUnsupported: [9p] From 69f223fe4fed2b18de2589a84967f17311e9334e Mon Sep 17 00:00:00 2001 From: Horiodino Date: Tue, 17 Jun 2025 13:33:10 +0530 Subject: [PATCH 43/91] reporting all the validation errors in the template Signed-off-by: Horiodino updatec test-cases Signed-off-by: Horiodino using err.join Signed-off-by: Horiodino --- pkg/limayaml/validate.go | 244 ++++++++++++++++++---------------- pkg/limayaml/validate_test.go | 35 ++++- 2 files changed, 161 insertions(+), 118 deletions(-) diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 8298882ce81..e625659d818 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -30,54 +30,58 @@ import ( ) func validateFileObject(f File, fieldName string) error { + var errs error if !strings.Contains(f.Location, "://") { if _, err := localpathutil.Expand(f.Location); err != nil { - return fmt.Errorf("field `%s.location` refers to an invalid local file path: %q: %w", fieldName, f.Location, err) + errs = errors.Join(errs, fmt.Errorf("field `%s.location` refers to an invalid local file path: %q: %w", fieldName, f.Location, err)) } // f.Location does NOT need to be accessible, so we do NOT check os.Stat(f.Location) } if !slices.Contains(ArchTypes, f.Arch) { - return fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch) + errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch)) } if f.Digest != "" { if !f.Digest.Algorithm().Available() { - return fmt.Errorf("field `%s.digest` refers to an unavailable digest algorithm", fieldName) + errs = errors.Join(errs, fmt.Errorf("field `%s.digest` refers to an unavailable digest algorithm", fieldName)) } if err := f.Digest.Validate(); err != nil { - return fmt.Errorf("field `%s.digest` is invalid: %s: %w", fieldName, f.Digest.String(), err) + errs = errors.Join(errs, fmt.Errorf("field `%s.digest` is invalid: %s: %w", fieldName, f.Digest.String(), err)) } } - return nil + return errs } func Validate(y *LimaYAML, warn bool) error { + var errs error + if len(y.Base) > 0 { - return errors.New("field `base` must be empty for YAML validation") + errs = errors.Join(errs, errors.New("field `base` must be empty for YAML validation")) } + if y.MinimumLimaVersion != nil { if _, err := versionutil.Parse(*y.MinimumLimaVersion); err != nil { - return fmt.Errorf("field `minimumLimaVersion` must be a semvar value, got %q: %w", *y.MinimumLimaVersion, err) + errs = errors.Join(errs, fmt.Errorf("field `minimumLimaVersion` must be a semvar value, got %q: %w", *y.MinimumLimaVersion, err)) } limaVersion, err := versionutil.Parse(version.Version) if err != nil { - return fmt.Errorf("can't parse builtin Lima version %q: %w", version.Version, err) + errs = errors.Join(errs, fmt.Errorf("can't parse builtin Lima version %q: %w", version.Version, err)) } if versionutil.GreaterThan(*y.MinimumLimaVersion, limaVersion.String()) { - return fmt.Errorf("template requires Lima version %q; this is only %q", *y.MinimumLimaVersion, limaVersion.String()) + errs = errors.Join(errs, fmt.Errorf("template requires Lima version %q; this is only %q", *y.MinimumLimaVersion, limaVersion.String())) } } if y.VMOpts.QEMU.MinimumVersion != nil { if _, err := semver.NewVersion(*y.VMOpts.QEMU.MinimumVersion); err != nil { - return fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semvar value, got %q: %w", *y.VMOpts.QEMU.MinimumVersion, err) + errs = errors.Join(errs, fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semvar value, got %q: %w", *y.VMOpts.QEMU.MinimumVersion, err)) } } switch *y.OS { case LINUX: default: - return fmt.Errorf("field `os` must be %q; got %q", LINUX, *y.OS) + errs = errors.Join(errs, fmt.Errorf("field `os` must be %q; got %q", LINUX, *y.OS)) } if !slices.Contains(ArchTypes, *y.Arch) { - return fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, *y.Arch) + errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, *y.Arch)) } switch *y.VMType { case QEMU: @@ -86,114 +90,114 @@ func Validate(y *LimaYAML, warn bool) error { // NOP case VZ: if !IsNativeArch(*y.Arch) { - return fmt.Errorf("field `arch` must be %q for VZ; got %q", NewArch(runtime.GOARCH), *y.Arch) + errs = errors.Join(errs, fmt.Errorf("field `arch` must be %q for VZ; got %q", NewArch(runtime.GOARCH), *y.Arch)) } default: - return fmt.Errorf("field `vmType` must be %q, %q, %q; got %q", QEMU, VZ, WSL2, *y.VMType) + errs = errors.Join(errs, fmt.Errorf("field `vmType` must be %q, %q, %q; got %q", QEMU, VZ, WSL2, *y.VMType)) } if len(y.Images) == 0 { - return errors.New("field `images` must be set") + errs = errors.Join(errs, errors.New("field `images` must be set")) } for i, f := range y.Images { - if err := validateFileObject(f.File, fmt.Sprintf("images[%d]", i)); err != nil { - return err + err := validateFileObject(f.File, fmt.Sprintf("images[%d]", i)) + if err != nil { + errs = errors.Join(errs, err) } if f.Kernel != nil { - if err := validateFileObject(f.Kernel.File, fmt.Sprintf("images[%d].kernel", i)); err != nil { - return err + err := validateFileObject(f.Kernel.File, fmt.Sprintf("images[%d].kernel", i)) + if err != nil { + errs = errors.Join(errs, err) } if f.Kernel.Arch != f.Arch { - return fmt.Errorf("images[%d].kernel has unexpected architecture %q, must be %q", i, f.Kernel.Arch, f.Arch) + errs = errors.Join(errs, fmt.Errorf("images[%d].kernel has unexpected architecture %q, must be %q", i, f.Kernel.Arch, f.Arch)) } } if f.Initrd != nil { - if err := validateFileObject(*f.Initrd, fmt.Sprintf("images[%d].initrd", i)); err != nil { - return err - } - if f.Kernel == nil { - return errors.New("initrd requires the kernel to be specified") + err := validateFileObject(*f.Initrd, fmt.Sprintf("images[%d].initrd", i)) + if err != nil { + errs = errors.Join(errs, err) } if f.Initrd.Arch != f.Arch { - return fmt.Errorf("images[%d].initrd has unexpected architecture %q, must be %q", i, f.Initrd.Arch, f.Arch) + errs = errors.Join(errs, fmt.Errorf("images[%d].initrd has unexpected architecture %q, must be %q", i, f.Initrd.Arch, f.Arch)) } } } for arch := range y.CPUType { if !slices.Contains(ArchTypes, arch) { - return fmt.Errorf("field `cpuType` uses unsupported arch %q", arch) + errs = errors.Join(errs, fmt.Errorf("field `cpuType` uses unsupported arch %q", arch)) } } if *y.CPUs == 0 { - return errors.New("field `cpus` must be set") + errs = errors.Join(errs, errors.New("field `cpus` must be set")) } if _, err := units.RAMInBytes(*y.Memory); err != nil { - return fmt.Errorf("field `memory` has an invalid value: %w", err) + errs = errors.Join(errs, fmt.Errorf("field `memory` has an invalid value: %w", err)) } if _, err := units.RAMInBytes(*y.Disk); err != nil { - return fmt.Errorf("field `memory` has an invalid value: %w", err) + errs = errors.Join(errs, fmt.Errorf("field `disk` has an invalid value: %w", err)) } for i, disk := range y.AdditionalDisks { if err := identifiers.Validate(disk.Name); err != nil { - return fmt.Errorf("field `additionalDisks[%d].name is invalid`: %w", i, err) + errs = errors.Join(errs, fmt.Errorf("field `additionalDisks[%d].name is invalid`: %w", i, err)) } } for i, f := range y.Mounts { if !filepath.IsAbs(f.Location) && !strings.HasPrefix(f.Location, "~") { - return fmt.Errorf("field `mounts[%d].location` must be an absolute path, got %q", - i, f.Location) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` must be an absolute path, got %q", + i, f.Location)) } // f.Location has already been expanded in FillDefaults(), but that function cannot return errors. loc, err := localpathutil.Expand(f.Location) if err != nil { - return fmt.Errorf("field `mounts[%d].location` refers to an unexpandable path: %q: %w", i, f.Location, err) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to an unexpandable path: %q: %w", i, f.Location, err)) } st, err := os.Stat(loc) if err != nil { if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("field `mounts[%d].location` refers to an inaccessible path: %q: %w", i, f.Location, err) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to an inaccessible path: %q: %w", i, f.Location, err)) } } else if !st.IsDir() { - return fmt.Errorf("field `mounts[%d].location` refers to a non-directory path: %q: %w", i, f.Location, err) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to a non-directory path: %q: %w", i, f.Location, err)) } switch *f.MountPoint { case "/", "/bin", "/dev", "/etc", "/home", "/opt", "/sbin", "/tmp", "/usr", "/var": - return fmt.Errorf("field `mounts[%d].mountPoint` must not be a system path such as /etc or /usr", i) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].mountPoint` must not be a system path such as /etc or /usr", i)) // home directory defined in "cidata.iso:/user-data" case *y.User.Home: - return fmt.Errorf("field `mounts[%d].mountPoint` is the reserved internal home directory %q", i, *y.User.Home) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].mountPoint` is the reserved internal home directory %q", i, *y.User.Home)) } // There is no tilde-expansion for guest filenames if strings.HasPrefix(*f.MountPoint, "~") { - return fmt.Errorf("field `mounts[%d].mountPoint` must not start with \"~\"", i) + errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].mountPoint` must not start with \"~\"", i)) } if _, err := units.RAMInBytes(*f.NineP.Msize); err != nil { - return fmt.Errorf("field `msize` has an invalid value: %w", err) + errs = errors.Join(errs, fmt.Errorf("field `msize` has an invalid value: %w", err)) } } if *y.SSH.LocalPort != 0 { if err := validatePort("ssh.localPort", *y.SSH.LocalPort); err != nil { - return err + errs = errors.Join(errs, err) } } switch *y.MountType { case REVSSHFS, NINEP, VIRTIOFS, WSLMount: default: - return fmt.Errorf("field `mountType` must be %q or %q or %q, or %q, got %q", REVSSHFS, NINEP, VIRTIOFS, WSLMount, *y.MountType) + errs = errors.Join(errs, fmt.Errorf("field `mountType` must be %q or %q or %q, or %q, got %q", REVSSHFS, NINEP, VIRTIOFS, WSLMount, *y.MountType)) } if slices.Contains(y.MountTypesUnsupported, *y.MountType) { - return fmt.Errorf("field `mountType` must not be one of %v (`mountTypesUnsupported`), got %q", y.MountTypesUnsupported, *y.MountType) + errs = errors.Join(errs, fmt.Errorf("field `mountType` must not be one of %v (`mountTypesUnsupported`), got %q", y.MountTypesUnsupported, *y.MountType)) } if warn && runtime.GOOS != "linux" { @@ -209,66 +213,69 @@ func Validate(y *LimaYAML, warn bool) error { for i, p := range y.Provision { if p.File != nil { if p.File.URL != "" { - return fmt.Errorf("field `provision[%d].file.url` must be empty during validation (script should already be embedded)", i) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].file.url` must be empty during validation (script should already be embedded)", i)) } if p.File.Digest != nil { - return fmt.Errorf("field `provision[%d].file.digest` support is not yet implemented", i) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].file.digest` support is not yet implemented", i)) } } switch p.Mode { case ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible: default: - return fmt.Errorf("field `provision[%d].mode` must one of %q, %q, %q, %q, %q, or %q", - i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].mode` must one of %q, %q, %q, %q, %q, or %q", + i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible)) } if p.Mode != ProvisionModeDependency && p.SkipDefaultDependencyResolution != nil { - return fmt.Errorf("field `provision[%d].mode` cannot set skipDefaultDependencyResolution, only valid on scripts of type %q", - i, ProvisionModeDependency) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].mode` cannot set skipDefaultDependencyResolution, only valid on scripts of type %q", + i, ProvisionModeDependency)) } + + // This can lead to fatal Panic if p.Path is nil, better to return an error here if p.Mode == ProvisionModeData { if p.Path == nil { - return fmt.Errorf("field `provision[%d].path` must not be empty when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` must not be empty when mode is %q", i, ProvisionModeData)) + return errs } if !path.IsAbs(*p.Path) { - return fmt.Errorf("field `provision[%d].path` must be an absolute path", i) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` must be an absolute path", i)) } if p.Content == nil { - return fmt.Errorf("field `provision[%d].content` must not be empty when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` must not be empty when mode is %q", i, ProvisionModeData)) } // FillDefaults makes sure that p.Permissions is not nil if _, err := strconv.ParseInt(*p.Permissions, 8, 64); err != nil { - return fmt.Errorf("field `provision[%d].permissions` must be an octal number: %w", i, err) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` must be an octal number: %w", i, err)) } } else { if p.Script == "" && p.Mode != ProvisionModeAnsible { - return fmt.Errorf("field `provision[%d].script` must not be empty", i) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].script` must not be empty", i)) } if p.Content != nil { - return fmt.Errorf("field `provision[%d].content` can only be set when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` can only be set when mode is %q", i, ProvisionModeData)) } if p.Overwrite != nil { - return fmt.Errorf("field `provision[%d].overwrite` can only be set when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].overwrite` can only be set when mode is %q", i, ProvisionModeData)) } if p.Owner != nil { - return fmt.Errorf("field `provision[%d].owner` can only be set when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].owner` can only be set when mode is %q", i, ProvisionModeData)) } if p.Path != nil { - return fmt.Errorf("field `provision[%d].path` can only be set when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` can only be set when mode is %q", i, ProvisionModeData)) } if p.Permissions != nil { - return fmt.Errorf("field `provision[%d].permissions` can only be set when mode is %q", i, ProvisionModeData) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` can only be set when mode is %q", i, ProvisionModeData)) } } if p.Playbook != "" { if p.Mode != ProvisionModeAnsible { - return fmt.Errorf("field `provision[%d].playbook can only be set when mode is %q", i, ProvisionModeAnsible) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook can only be set when mode is %q", i, ProvisionModeAnsible)) } if p.Script != "" { - return fmt.Errorf("field `provision[%d].script must be empty if playbook is set", i) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].script must be empty if playbook is set", i)) } playbook := p.Playbook if _, err := os.Stat(playbook); err != nil { - return fmt.Errorf("field `provision[%d].playbook` refers to an inaccessible path: %q: %w", i, playbook, err) + errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook` refers to an inaccessible path: %q: %w", i, playbook, err)) } logrus.Warnf("provision mode %q is deprecated, use `ansible-playbook %q` instead", ProvisionModeAnsible, playbook) } @@ -279,109 +286,110 @@ func Validate(y *LimaYAML, warn bool) error { needsContainerdArchives := (y.Containerd.User != nil && *y.Containerd.User) || (y.Containerd.System != nil && *y.Containerd.System) if needsContainerdArchives { if len(y.Containerd.Archives) == 0 { - return errors.New("field `containerd.archives` must be provided") + errs = errors.Join(errs, errors.New("field `containerd.archives` must be provided")) } for i, f := range y.Containerd.Archives { - if err := validateFileObject(f, fmt.Sprintf("containerd.archives[%d]", i)); err != nil { - return err + err := validateFileObject(f, fmt.Sprintf("containerd.archives[%d]", i)) + if err != nil { + errs = errors.Join(errs, err) } } } for i, p := range y.Probes { if p.File != nil { if p.File.URL != "" { - return fmt.Errorf("field `probe[%d].file.url` must be empty during validation (script should already be embedded)", i) + errs = errors.Join(errs, fmt.Errorf("field `probe[%d].file.url` must be empty during validation (script should already be embedded)", i)) } if p.File.Digest != nil { - return fmt.Errorf("field `probe[%d].file.digest` support is not yet implemented", i) + errs = errors.Join(errs, fmt.Errorf("field `probe[%d].file.digest` support is not yet implemented", i)) } } if !strings.HasPrefix(p.Script, "#!") { - return fmt.Errorf("field `probe[%d].script` must start with a '#!' line", i) + errs = errors.Join(errs, fmt.Errorf("field `probe[%d].script` must start with a '#!' line", i)) } switch p.Mode { case ProbeModeReadiness: default: - return fmt.Errorf("field `probe[%d].mode` can only be %q", i, ProbeModeReadiness) + errs = errors.Join(errs, fmt.Errorf("field `probe[%d].mode` can only be %q", i, ProbeModeReadiness)) } } for i, rule := range y.PortForwards { field := fmt.Sprintf("portForwards[%d]", i) if rule.GuestIPMustBeZero && !rule.GuestIP.Equal(net.IPv4zero) { - return fmt.Errorf("field `%s.guestIPMustBeZero` can only be true when field `%s.guestIP` is 0.0.0.0", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestIPMustBeZero` can only be true when field `%s.guestIP` is 0.0.0.0", field, field)) } if rule.GuestPort != 0 { if rule.GuestSocket != "" { - return fmt.Errorf("field `%s.guestPort` must be 0 when field `%s.guestSocket` is set", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestPort` must be 0 when field `%s.guestSocket` is set", field, field)) } if rule.GuestPort != rule.GuestPortRange[0] { - return fmt.Errorf("field `%s.guestPort` must match field `%s.guestPortRange[0]`", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestPort` must match field `%s.guestPortRange[0]`", field, field)) } // redundant validation to make sure the error contains the correct field name if err := validatePort(field+".guestPort", rule.GuestPort); err != nil { - return err + errs = errors.Join(errs, err) } } if rule.HostPort != 0 { if rule.HostSocket != "" { - return fmt.Errorf("field `%s.hostPort` must be 0 when field `%s.hostSocket` is set", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostPort` must be 0 when field `%s.hostSocket` is set", field, field)) } if rule.HostPort != rule.HostPortRange[0] { - return fmt.Errorf("field `%s.hostPort` must match field `%s.hostPortRange[0]`", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostPort` must match field `%s.hostPortRange[0]`", field, field)) } // redundant validation to make sure the error contains the correct field name if err := validatePort(field+".hostPort", rule.HostPort); err != nil { - return err + errs = errors.Join(errs, err) } } for j := range 2 { if err := validatePort(fmt.Sprintf("%s.guestPortRange[%d]", field, j), rule.GuestPortRange[j]); err != nil { - return err + errs = errors.Join(errs, err) } if err := validatePort(fmt.Sprintf("%s.hostPortRange[%d]", field, j), rule.HostPortRange[j]); err != nil { - return err + errs = errors.Join(errs, err) } } if rule.GuestPortRange[0] > rule.GuestPortRange[1] { - return fmt.Errorf("field `%s.guestPortRange[1]` must be greater than or equal to field `%s.guestPortRange[0]`", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestPortRange[1]` must be greater than or equal to field `%s.guestPortRange[0]`", field, field)) } if rule.HostPortRange[0] > rule.HostPortRange[1] { - return fmt.Errorf("field `%s.hostPortRange[1]` must be greater than or equal to field `%s.hostPortRange[0]`", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange[1]` must be greater than or equal to field `%s.hostPortRange[0]`", field, field)) } if rule.GuestPortRange[1]-rule.GuestPortRange[0] != rule.HostPortRange[1]-rule.HostPortRange[0] { - return fmt.Errorf("field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`", field, field)) } if rule.GuestSocket != "" { if !path.IsAbs(rule.GuestSocket) { - return fmt.Errorf("field `%s.guestSocket` must be an absolute path, but is %q", field, rule.GuestSocket) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestSocket` must be an absolute path, but is %q", field, rule.GuestSocket)) } if rule.HostSocket == "" && rule.HostPortRange[1]-rule.HostPortRange[0] > 0 { - return fmt.Errorf("field `%s.guestSocket` can only be mapped to a single port or socket. not a range", field) + errs = errors.Join(errs, fmt.Errorf("field `%s.guestSocket` can only be mapped to a single port or socket. not a range", field)) } } if rule.HostSocket != "" { if !filepath.IsAbs(rule.HostSocket) { // should be unreachable because FillDefault() will prepend the instance directory to relative names - return fmt.Errorf("field `%s.hostSocket` must be an absolute path, but is %q", field, rule.HostSocket) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` must be an absolute path, but is %q", field, rule.HostSocket)) } if rule.GuestSocket == "" && rule.GuestPortRange[1]-rule.GuestPortRange[0] > 0 { - return fmt.Errorf("field `%s.hostSocket` can only be mapped from a single port or socket. not a range", field) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` can only be mapped from a single port or socket. not a range", field)) } } if len(rule.HostSocket) >= osutil.UnixPathMax { - return fmt.Errorf("field `%s.hostSocket` must be less than UNIX_PATH_MAX=%d characters, but is %d", - field, osutil.UnixPathMax, len(rule.HostSocket)) + errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` must be less than UNIX_PATH_MAX=%d characters, but is %d", + field, osutil.UnixPathMax, len(rule.HostSocket))) } switch rule.Proto { case ProtoTCP, ProtoUDP, ProtoAny: default: - return fmt.Errorf("field `%s.proto` must be %q, %q, or %q", field, ProtoTCP, ProtoUDP, ProtoAny) + errs = errors.Join(errs, fmt.Errorf("field `%s.proto` must be %q, %q, or %q", field, ProtoTCP, ProtoUDP, ProtoAny)) } if rule.Reverse && rule.GuestSocket == "" { - return fmt.Errorf("field `%s.reverse` must be %t", field, false) + errs = errors.Join(errs, fmt.Errorf("field `%s.reverse` must be %t", field, false)) } if rule.Reverse && rule.HostSocket == "" { - return fmt.Errorf("field `%s.reverse` must be %t", field, false) + errs = errors.Join(errs, fmt.Errorf("field `%s.reverse` must be %t", field, false)) } // Not validating that the various GuestPortRanges and HostPortRanges are not overlapping. Rules will be // processed sequentially and the first matching rule for a guest port determines forwarding behavior. @@ -390,23 +398,25 @@ func Validate(y *LimaYAML, warn bool) error { field := fmt.Sprintf("CopyToHost[%d]", i) if rule.GuestFile != "" { if !path.IsAbs(rule.GuestFile) { - return fmt.Errorf("field `%s.guest` must be an absolute path, but is %q", field, rule.GuestFile) + errs = errors.Join(errs, fmt.Errorf("field `%s.guest` must be an absolute path, but is %q", field, rule.GuestFile)) } } if rule.HostFile != "" { if !filepath.IsAbs(rule.HostFile) { - return fmt.Errorf("field `%s.host` must be an absolute path, but is %q", field, rule.HostFile) + errs = errors.Join(errs, fmt.Errorf("field `%s.host` must be an absolute path, but is %q", field, rule.HostFile)) } } } if y.HostResolver.Enabled != nil && *y.HostResolver.Enabled && len(y.DNS) > 0 { - return errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true") + errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true")) } - if err := validateNetwork(y); err != nil { - return err + err := validateNetwork(y) + if err != nil { + errs = errors.Join(errs, err) } + if warn { warnExperimental(y) } @@ -416,19 +426,20 @@ func Validate(y *LimaYAML, warn bool) error { validParamName := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`) for param, value := range y.Param { if !validParamName.MatchString(param) { - return fmt.Errorf("param %q name does not match regex %q", param, validParamName.String()) + errs = errors.Join(errs, fmt.Errorf("param %q name does not match regex %q", param, validParamName.String())) } for _, r := range value { if !unicode.IsPrint(r) && r != '\t' && r != ' ' { - return fmt.Errorf("param %q value contains unprintable character %q", param, r) + errs = errors.Join(errs, fmt.Errorf("param %q value contains unprintable character %q", param, r)) } } } - return nil + return errs } func validateNetwork(y *LimaYAML) error { + var errs error interfaceName := make(map[string]int) for i, nw := range y.Networks { field := fmt.Sprintf("networks[%d]", i) @@ -439,68 +450,69 @@ func validateNetwork(y *LimaYAML) error { return err } if nwCfg.Check(nw.Lima) != nil { - return fmt.Errorf("field `%s.lima` references network %q which is not defined in networks.yaml", field, nw.Lima) + errs = errors.Join(errs, fmt.Errorf("field `%s.lima` references network %q which is not defined in networks.yaml", field, nw.Lima)) } usernet, err := nwCfg.Usernet(nw.Lima) if err != nil { return err } if !usernet && runtime.GOOS != "darwin" { - return fmt.Errorf("field `%s.lima` is only supported on macOS right now", field) + errs = errors.Join(errs, fmt.Errorf("field `%s.lima` is only supported on macOS right now", field)) } if nw.Socket != "" { - return fmt.Errorf("field `%s.lima` and field `%s.socket` are mutually exclusive", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.lima` and field `%s.socket` are mutually exclusive", field, field)) } if nw.VZNAT != nil && *nw.VZNAT { - return fmt.Errorf("field `%s.lima` and field `%s.vzNAT` are mutually exclusive", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.lima` and field `%s.vzNAT` are mutually exclusive", field, field)) } case nw.Socket != "": if nw.VZNAT != nil && *nw.VZNAT { - return fmt.Errorf("field `%s.socket` and field `%s.vzNAT` are mutually exclusive", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.socket` and field `%s.vzNAT` are mutually exclusive", field, field)) } if fi, err := os.Stat(nw.Socket); err != nil && !errors.Is(err, os.ErrNotExist) { - return err + errs = errors.Join(errs, err) } else if err == nil && fi.Mode()&os.ModeSocket == 0 { - return fmt.Errorf("field `%s.socket` %q points to a non-socket file", field, nw.Socket) + errs = errors.Join(errs, fmt.Errorf("field `%s.socket` %q points to a non-socket file", field, nw.Socket)) } case nw.VZNAT != nil && *nw.VZNAT: if y.VMType == nil || *y.VMType != VZ { - return fmt.Errorf("field `%s.vzNAT` requires `vmType` to be %q", field, VZ) + errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` requires `vmType` to be %q", field, VZ)) } if nw.Lima != "" { - return fmt.Errorf("field `%s.vzNAT` and field `%s.lima` are mutually exclusive", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` and field `%s.lima` are mutually exclusive", field, field)) } if nw.Socket != "" { - return fmt.Errorf("field `%s.vzNAT` and field `%s.socket` are mutually exclusive", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` and field `%s.socket` are mutually exclusive", field, field)) } default: - return fmt.Errorf("field `%s.lima` or field `%s.socket must be set", field, field) + errs = errors.Join(errs, fmt.Errorf("field `%s.lima` or field `%s.socket must be set", field, field)) } if nw.MACAddress != "" { hw, err := net.ParseMAC(nw.MACAddress) if err != nil { - return fmt.Errorf("field `vmnet.mac` invalid: %w", err) + errs = errors.Join(errs, fmt.Errorf("field `vmnet.mac` invalid: %w", err)) } if len(hw) != 6 { - return fmt.Errorf("field `%s.macAddress` must be a 48 bit (6 bytes) MAC address; actual length of %q is %d bytes", field, nw.MACAddress, len(hw)) + errs = errors.Join(errs, fmt.Errorf("field `%s.macAddress` must be a 48 bit (6 bytes) MAC address; actual length of %q is %d bytes", field, nw.MACAddress, len(hw))) } } // FillDefault() will make sure that nw.Interface is not the empty string if len(nw.Interface) >= 16 { - return fmt.Errorf("field `%s.interface` must be less than 16 bytes, but is %d bytes: %q", field, len(nw.Interface), nw.Interface) + errs = errors.Join(errs, fmt.Errorf("field `%s.interface` must be less than 16 bytes, but is %d bytes: %q", field, len(nw.Interface), nw.Interface)) } if strings.ContainsAny(nw.Interface, " \t\n/") { - return fmt.Errorf("field `%s.interface` must not contain whitespace or slashes", field) + errs = errors.Join(errs, fmt.Errorf("field `%s.interface` must not contain whitespace or slashes", field)) } if nw.Interface == networks.SlirpNICName { - return fmt.Errorf("field `%s.interface` must not be set to %q because it is reserved for slirp", field, networks.SlirpNICName) + errs = errors.Join(errs, fmt.Errorf("field `%s.interface` must not be set to %q because it is reserved for slirp", field, networks.SlirpNICName)) } if prev, ok := interfaceName[nw.Interface]; ok { - return fmt.Errorf("field `%s.interface` value %q has already been used by field `networks[%d].interface`", field, nw.Interface, prev) + errs = errors.Join(errs, fmt.Errorf("field `%s.interface` value %q has already been used by field `networks[%d].interface`", field, nw.Interface, prev)) } interfaceName[nw.Interface] = i } - return nil + + return errs } // ValidateParamIsUsed checks if the keys in the `param` field are used in any script, probe, copyToHost, or portForward. diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index d23db80261b..859d8ea9e01 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -37,7 +37,8 @@ func TestValidateProbes(t *testing.T) { assert.NilError(t, err) err = Validate(y, false) - assert.Error(t, err, "field `probe[0].file.digest` support is not yet implemented") + assert.Error(t, err, "field `probe[0].file.digest` support is not yet implemented\n"+ + "field `probe[0].script` must start with a '#!' line") } func TestValidateProvisionMode(t *testing.T) { @@ -68,7 +69,8 @@ func TestValidateProvisionMode(t *testing.T) { assert.NilError(t, err) err = Validate(y, false) - assert.Error(t, err, "field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"") + assert.Error(t, err, "field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+ + "field `provision[0].script` must not be empty") } func TestValidateProvisionData(t *testing.T) { @@ -233,3 +235,32 @@ func TestValidateParamIsUsed(t *testing.T) { assert.Error(t, err, "field `param` key \"rootFul\" is not used in any provision, probe, copyToHost, or portForward") } } + +func TestValidateMultipleErrors(t *testing.T) { + yamlWithMultipleErrors := ` +os: windows +arch: unsupported_arch +vmType: invalid_type +portForwards: + - guestPort: 22 + hostPort: 2222 + - guestPort: 8080 + hostPort: 65536 +provision: + - mode: invalid_mode + script: echo test + - mode: data + content: test +` + + y, err := Load([]byte(yamlWithMultipleErrors), "multiple-errors.yaml") + assert.NilError(t, err) + err = Validate(y, false) + + assert.Error(t, err, "field `os` must be \"Linux\"; got \"windows\"\n"+ + "field `arch` must be one of [x86_64 aarch64 armv7l ppc64le riscv64 s390x]; got \"unsupported_arch\"\n"+ + "field `vmType` must be \"qemu\", \"vz\", \"wsl2\"; got \"invalid_type\"\n"+ + "field `images` must be set\n"+ + "field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+ + "field `provision[1].path` must not be empty when mode is \"data\"") +} From 0b39b440c74f0ba91607c3bc01dfdccd45b8282d Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 16:41:21 +0900 Subject: [PATCH 44/91] templates: add experimental/debian-testing Fix issue 3629 Signed-off-by: Akihiro Suda --- templates/README.md | 1 + templates/experimental/debian-13.yaml | 15 +++++++++++++++ templates/experimental/debian-testing.yaml | 1 + 3 files changed, 17 insertions(+) create mode 100644 templates/experimental/debian-13.yaml create mode 120000 templates/experimental/debian-testing.yaml diff --git a/templates/README.md b/templates/README.md index da51eeedda5..af4752924f6 100644 --- a/templates/README.md +++ b/templates/README.md @@ -34,6 +34,7 @@ Distro: - [`experimental/gentoo`](./experimental/gentoo.yaml): [experimental] Gentoo - [`experimental/opensuse-tumbleweed`](./experimental/opensuse-tumbleweed.yaml): [experimental] openSUSE Tumbleweed - [`experimental/debian-sid`](./experimental/debian-sid.yaml): [experimental] Debian Sid +- [`experimental/debian-13`](./experimental/debian-13.yaml), `experimental/debian-testing`: [experimental] Debian GNU/Linux 13 (Trixie) Alternative package managers: - [`linuxbrew.yaml`](./linuxbrew.yaml): [Homebrew](https://brew.sh) on Linux (Ubuntu) diff --git a/templates/experimental/debian-13.yaml b/templates/experimental/debian-13.yaml new file mode 100644 index 00000000000..242058e8b98 --- /dev/null +++ b/templates/experimental/debian-13.yaml @@ -0,0 +1,15 @@ +minimumLimaVersion: 1.1.0 + +base: template://_default/mounts + +images: +- location: https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.qcow2 + arch: x86_64 + +- location: https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-arm64-daily.qcow2 + arch: aarch64 + +- location: https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-ppc64el-daily.qcow2 + arch: ppc64le + +mountTypesUnsupported: [9p] diff --git a/templates/experimental/debian-testing.yaml b/templates/experimental/debian-testing.yaml new file mode 120000 index 00000000000..99e7fcf3cdf --- /dev/null +++ b/templates/experimental/debian-testing.yaml @@ -0,0 +1 @@ +debian-13.yaml \ No newline at end of file From 8ef2988829a5ef047f3a68c2427a5eea4449505e Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 16:48:35 +0900 Subject: [PATCH 45/91] templates: add experimental/ubuntu-next Signed-off-by: Akihiro Suda --- templates/README.md | 1 + templates/experimental/ubuntu-25.10.yaml | 17 +++++++++++++++++ templates/experimental/ubuntu-next.yaml | 1 + 3 files changed, 19 insertions(+) create mode 100644 templates/experimental/ubuntu-25.10.yaml create mode 120000 templates/experimental/ubuntu-next.yaml diff --git a/templates/README.md b/templates/README.md index af4752924f6..6008eff3de5 100644 --- a/templates/README.md +++ b/templates/README.md @@ -31,6 +31,7 @@ Distro: - [`rocky-9`](./rocky-9.yaml), `rocky.yaml`: Rocky Linux 9 - [`ubuntu`](./ubuntu.yaml): Ubuntu (same as `default.yaml` but without extra YAML lines) - [`ubuntu-lts`](./ubuntu-lts.yaml): Ubuntu LTS (same as `ubuntu.yaml` but pinned to an LTS version) +- [`experimental/ubuntu-next`](./experimental/ubuntu-next.yaml): Ubuntu vNext - [`experimental/gentoo`](./experimental/gentoo.yaml): [experimental] Gentoo - [`experimental/opensuse-tumbleweed`](./experimental/opensuse-tumbleweed.yaml): [experimental] openSUSE Tumbleweed - [`experimental/debian-sid`](./experimental/debian-sid.yaml): [experimental] Debian Sid diff --git a/templates/experimental/ubuntu-25.10.yaml b/templates/experimental/ubuntu-25.10.yaml new file mode 100644 index 00000000000..0dda6f811ee --- /dev/null +++ b/templates/experimental/ubuntu-25.10.yaml @@ -0,0 +1,17 @@ +minimumLimaVersion: 1.1.0 + +base: template://_default/mounts + +images: +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-amd64.img + arch: x86_64 +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-arm64.img + arch: aarch64 +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-armhf.img + arch: armv7l +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-riscv64.img + arch: riscv64 +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-s390x.img + arch: s390x +- location: https://cloud-images.ubuntu.com/questing/current/questing-server-cloudimg-ppc64el.img + arch: ppc64le diff --git a/templates/experimental/ubuntu-next.yaml b/templates/experimental/ubuntu-next.yaml new file mode 120000 index 00000000000..fd760889f41 --- /dev/null +++ b/templates/experimental/ubuntu-next.yaml @@ -0,0 +1 @@ +ubuntu-25.10.yaml \ No newline at end of file From 352c051a2a86b65fc51a80acc8c379a8c04727b8 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Fri, 20 Jun 2025 15:24:14 +0300 Subject: [PATCH 46/91] Remove unused textutil.TrimString Signed-off-by: Oleksandr Redko --- pkg/textutil/textutil.go | 5 ----- pkg/textutil/textutil_test.go | 5 ----- 2 files changed, 10 deletions(-) diff --git a/pkg/textutil/textutil.go b/pkg/textutil/textutil.go index e02e63ad598..29d4d1bda76 100644 --- a/pkg/textutil/textutil.go +++ b/pkg/textutil/textutil.go @@ -44,11 +44,6 @@ func IndentString(size int, text string) string { return PrefixString(prefix, text) } -// TrimString removes characters from beginning and end. -func TrimString(cutset, text string) string { - return strings.Trim(text, cutset) -} - // MissingString returns message if the text is empty. func MissingString(message, text string) string { if text == "" { diff --git a/pkg/textutil/textutil_test.go b/pkg/textutil/textutil_test.go index b8784ece3e7..2605a114058 100644 --- a/pkg/textutil/textutil_test.go +++ b/pkg/textutil/textutil_test.go @@ -24,11 +24,6 @@ func TestIndentString(t *testing.T) { assert.Equal(t, " foo\n bar\n", IndentString(2, "foo\nbar\n")) } -func TestTrimString(t *testing.T) { - assert.Equal(t, "foo", TrimString("\n", "foo")) - assert.Equal(t, "bar", TrimString("\n", "bar\n")) -} - func TestMissingString(t *testing.T) { assert.Equal(t, "no", MissingString("no", "")) assert.Equal(t, "msg", MissingString("no", "msg")) From 63c89125eb4121116f019dcad3c5ecce308afb96 Mon Sep 17 00:00:00 2001 From: Songpon Srisawai Date: Sat, 31 May 2025 17:24:34 +0700 Subject: [PATCH 47/91] add warning log when non-existent directory Signed-off-by: Songpon Srisawai Co-authored-by: Akihiro Suda Co-authored-by: Jan Dubois --- pkg/limayaml/validate.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index e625659d818..05aac46821d 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -163,6 +163,9 @@ func Validate(y *LimaYAML, warn bool) error { if !errors.Is(err, os.ErrNotExist) { errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to an inaccessible path: %q: %w", i, f.Location, err)) } + if warn { + logrus.Warnf("field `mounts[%d].location` refers to a non-existent directory: %q:", i, f.Location) + } } else if !st.IsDir() { errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to a non-directory path: %q: %w", i, f.Location, err)) } From 45e8e9b48a38862bd78979e95c5110cfe1fe5010 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sat, 21 Jun 2025 09:20:04 +0900 Subject: [PATCH 48/91] editflags: add `--mount-none` An alias of `--set ".mounts = null"` Signed-off-by: Akihiro Suda --- cmd/limactl/editflags/editflags.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmd/limactl/editflags/editflags.go b/cmd/limactl/editflags/editflags.go index 813b263ab95..ae9fa593e4f 100644 --- a/cmd/limactl/editflags/editflags.go +++ b/cmd/limactl/editflags/editflags.go @@ -4,6 +4,7 @@ package editflags import ( + "errors" "fmt" "math/bits" "runtime" @@ -45,6 +46,7 @@ func registerEdit(cmd *cobra.Command, commentPrefix string) { }) flags.StringSlice("mount", nil, commentPrefix+"Directories to mount, suffix ':w' for writable (Do not specify directories that overlap with the existing mounts)") // colima-compatible + flags.Bool("mount-none", false, commentPrefix+"Remove all mounts") flags.String("mount-type", "", commentPrefix+"Mount type (reverse-sshfs, 9p, virtiofs)") // Similar to colima's --mount-type=(sshfs|9p|virtiofs), but "reverse-sshfs" is Lima is called "sshfs" in colima _ = cmd.RegisterFlagCompletionFunc("mount-type", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { @@ -157,6 +159,21 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) { false, false, }, + { + "mount-none", + func(_ *flag.Flag) (string, error) { + ss, err := flags.GetStringSlice("mount") + if err != nil { + return "", err + } + if len(ss) > 0 { + return "", errors.New("flag `--mount-none` conflicts with `--mount`") + } + return ".mounts = null", nil + }, + false, + false, + }, {"mount-type", d(".mountType = %q"), false, false}, {"mount-inotify", d(".mountInotify = %s"), false, true}, {"mount-writable", d(".mounts[].writable = %s"), false, false}, From c13b75d0d4c1ac8eb3169e00f1c2c497b8407617 Mon Sep 17 00:00:00 2001 From: Songpon Srisawai Date: Fri, 30 May 2025 11:27:22 +0700 Subject: [PATCH 49/91] reject shrinking disk during YAML validation Signed-off-by: Songpon Srisawai Co-authored-by: Jan Dubois Co-authored-by: Oleksandr Redko --- cmd/limactl/edit.go | 22 +++++++++++---- pkg/limayaml/validate.go | 37 ++++++++++++++++++++++++ pkg/limayaml/validate_test.go | 53 +++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index 33ee9581333..9c57e18a8a0 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -118,13 +118,13 @@ func editAction(cmd *cobra.Command, args []string) error { return err } if err := limayaml.Validate(y, true); err != nil { - rejectedYAML := "lima.REJECTED.yaml" - if writeErr := os.WriteFile(rejectedYAML, yBytes, 0o644); writeErr != nil { - return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w: %w", rejectedYAML, writeErr, err) - } - // TODO: may need to support editing the rejected YAML - return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, err) + return saveRejectedYAML(yBytes, err) + } + + if err := limayaml.ValidateYAMLAgainstLatestConfig(yBytes, yContent); err != nil { + return saveRejectedYAML(yBytes, err) } + if err := os.WriteFile(filePath, yBytes, 0o644); err != nil { return err } @@ -171,3 +171,13 @@ func askWhetherToStart() (bool, error) { func editBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return bashCompleteInstanceNames(cmd) } + +// saveRejectedYAML writes the rejected config and returns an error. +func saveRejectedYAML(y []byte, origErr error) error { + rejectedYAML := "lima.REJECTED.yaml" + if writeErr := os.WriteFile(rejectedYAML, y, 0o644); writeErr != nil { + return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %w", rejectedYAML, errors.Join(writeErr, origErr)) + } + // TODO: may need to support editing the rejected YAML + return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, origErr) +} diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 05aac46821d..443eaff02f7 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -610,3 +610,40 @@ func warnExperimental(y *LimaYAML) { logrus.Warn("`mountInotify` is experimental") } } + +// ValidateYAMLAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML. +// This validates configuration rules that disallow certain changes, such as shrinking the disk. +func ValidateYAMLAgainstLatestConfig(yNew, yLatest []byte) error { + var n LimaYAML + + // Load the latest YAML and fill in defaults + l, err := LoadWithWarnings(yLatest, "") + if err != nil { + return err + } + if err := Unmarshal(yNew, &n, "Unmarshal new YAML bytes"); err != nil { + return err + } + + // Handle editing the template without a disk value + if n.Disk == nil || l.Disk == nil { + return nil + } + + // Disk value must be provided, as it is required when creating an instance. + nDisk, err := units.RAMInBytes(*n.Disk) + if err != nil { + return err + } + lDisk, err := units.RAMInBytes(*l.Disk) + if err != nil { + return err + } + + // Reject shrinking disk + if nDisk < lDisk { + return fmt.Errorf("field `disk`: shrinking the disk (%v --> %v) is not supported", *l.Disk, *n.Disk) + } + + return nil +} diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index 859d8ea9e01..e136c6cbe06 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -4,6 +4,7 @@ package limayaml import ( + "errors" "testing" "gotest.tools/v3/assert" @@ -264,3 +265,55 @@ provision: "field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+ "field `provision[1].path` must not be empty when mode is \"data\"") } + +func TestValidateYAMLAgainstLatestConfig(t *testing.T) { + tests := []struct { + name string + yNew string + yLatest string + wantErr error + }{ + { + name: "Valid disk size unchanged", + yNew: `disk: 100GiB`, + yLatest: `disk: 100GiB`, + }, + { + name: "Valid disk size increased", + yNew: `disk: 200GiB`, + yLatest: `disk: 100GiB`, + }, + { + name: "No disk field in both YAMLs", + yNew: ``, + yLatest: ``, + }, + { + name: "No disk field in new YAMLs", + yNew: ``, + yLatest: `disk: 100GiB`, + }, + { + name: "No disk field in latest YAMLs", + yNew: `disk: 100GiB`, + yLatest: ``, + }, + { + name: "Disk size shrunk", + yNew: `disk: 50GiB`, + yLatest: `disk: 100GiB`, + wantErr: errors.New("field `disk`: shrinking the disk (100GiB --> 50GiB) is not supported"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateYAMLAgainstLatestConfig([]byte(tt.yNew), []byte(tt.yLatest)) + if tt.wantErr == nil { + assert.NilError(t, err) + } else { + assert.Error(t, err, tt.wantErr.Error()) + } + }) + } +} From b1ebd2ec01944035df6641a286643aefbd466123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 23 Jun 2025 07:06:13 +0200 Subject: [PATCH 50/91] Embed templates selected from the user interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders F Björklund --- cmd/limactl/start.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index 3f90f66ee4b..0d78bc1e0cd 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -4,6 +4,7 @@ package main import ( + "context" "errors" "fmt" "os" @@ -223,7 +224,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (* yq := yqutil.Join(yqExprs) if tty { var err error - tmpl, err = chooseNextCreatorState(tmpl, yq) + tmpl, err = chooseNextCreatorState(cmd.Context(), tmpl, yq) if err != nil { return nil, err } @@ -294,7 +295,7 @@ func (exitSuccessError) ExitCode() int { return 0 } -func chooseNextCreatorState(tmpl *limatmpl.Template, yq string) (*limatmpl.Template, error) { +func chooseNextCreatorState(ctx context.Context, tmpl *limatmpl.Template, yq string) (*limatmpl.Template, error) { for { if err := modifyInPlace(tmpl, yq); err != nil { logrus.WithError(err).Warn("Failed to evaluate yq expression") @@ -361,7 +362,11 @@ func chooseNextCreatorState(tmpl *limatmpl.Template, yq string) (*limatmpl.Templ return nil, err } } - tmpl.Bytes, err = os.ReadFile(yamlPath) + tmpl, err = limatmpl.Read(ctx, tmpl.Name, yamlPath) + if err != nil { + return nil, err + } + err = tmpl.Embed(ctx, true, true) if err != nil { return nil, err } From 7c062a4cb19e336cc0fb1ba4875e79d775053ba5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 01:12:23 +0000 Subject: [PATCH 51/91] build(deps): bump github.com/google/yamlfmt from 0.17.1 to 0.17.2 Bumps [github.com/google/yamlfmt](https://github.com/google/yamlfmt) from 0.17.1 to 0.17.2. - [Release notes](https://github.com/google/yamlfmt/releases) - [Changelog](https://github.com/google/yamlfmt/blob/main/.goreleaser.yaml) - [Commits](https://github.com/google/yamlfmt/compare/v0.17.1...v0.17.2) --- updated-dependencies: - dependency-name: github.com/google/yamlfmt dependency-version: 0.17.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 14a53959ed7..3747bc3217f 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/foxcpp/go-mockdns v1.1.0 github.com/goccy/go-yaml v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/google/yamlfmt v0.17.1 + github.com/google/yamlfmt v0.17.2 github.com/invopop/jsonschema v0.13.0 github.com/lima-vm/go-qcow2reader v0.6.0 github.com/lima-vm/sshocker v0.3.8 // gomodjail:unconfined diff --git a/go.sum b/go.sum index fb72ed4dcaf..2f211757334 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/yamlfmt v0.17.1 h1:NgKi21+MUVntxhPhIsstylN6QNCCfDv3wKNKwlm/3Hs= -github.com/google/yamlfmt v0.17.1/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= +github.com/google/yamlfmt v0.17.2 h1:TkXxhmj7dnpmOnlWGOXog92Gs6MWcTZqnf3kuyp8yFQ= +github.com/google/yamlfmt v0.17.2/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= From 8c09d70573d605745f560d54422f523923d59d3d Mon Sep 17 00:00:00 2001 From: Kaede Akino Date: Wed, 25 Jun 2025 08:49:43 +0800 Subject: [PATCH 52/91] Search for the data directory using real executable on non-Linux Signed-off-by: Kaede Akino --- pkg/usrlocalsharelima/usrlocalsharelima.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go index 0c8e99c7f84..0f08e609486 100644 --- a/pkg/usrlocalsharelima/usrlocalsharelima.go +++ b/pkg/usrlocalsharelima/usrlocalsharelima.go @@ -57,8 +57,16 @@ func Dir() (string, error) { selfViaOS, err := os.Executable() if err != nil { logrus.WithError(err).Warn("failed to find os.Executable()") - } else if len(selfPaths) == 0 || selfViaOS != selfPaths[0] { - selfPaths = append(selfPaths, selfViaOS) + } else { + selfFinalPathViaOS, err := filepath.EvalSymlinks(selfViaOS) + if err != nil { + logrus.WithError(err).Warn("failed to resolve symlinks") + selfFinalPathViaOS = selfViaOS // fallback to the original path + } + + if len(selfPaths) == 0 || selfFinalPathViaOS != selfPaths[0] { + selfPaths = append(selfPaths, selfFinalPathViaOS) + } } ostype := limayaml.NewOS("linux") From 7a277c47740ff94b0be177250eaaec11530012d3 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sat, 21 Jun 2025 08:13:31 +0900 Subject: [PATCH 53/91] limayaml: windows: disable 9p QEMU for Windows does not support 9p Signed-off-by: Akihiro Suda --- pkg/limayaml/defaults.go | 6 ++++++ templates/default.yaml | 2 +- website/content/en/docs/config/mount.md | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index a2193a28389..d9d2c6229d3 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -699,6 +699,12 @@ func FillDefault(y, d, o *LimaYAML, filePath string, warn bool) { for _, f := range y.MountTypesUnsupported { mountTypesUnsupported[f] = struct{}{} } + + if runtime.GOOS == "windows" { + // QEMU for Windows does not support 9p + mountTypesUnsupported[NINEP] = struct{}{} + } + // MountType has to be resolved before resolving Mounts if y.MountType == nil { y.MountType = d.MountType diff --git a/templates/default.yaml b/templates/default.yaml index 99f01eccdf0..344072a6b7e 100644 --- a/templates/default.yaml +++ b/templates/default.yaml @@ -97,7 +97,7 @@ mountTypesUnsupported: # Mount type for above mounts, such as "reverse-sshfs" (from sshocker), "9p" (QEMU’s virtio-9p-pci, aka virtfs), # or "virtiofs" (experimental on Linux; needs `vmType: vz` on macOS). -# 🟢 Builtin default: "default" (resolved to be "9p" for QEMU since Lima v1.0, "virtiofs" for vz) +# 🟢 Builtin default: "default" (resolved to be "9p" for QEMU since Lima v1.0 on non-Windows, "virtiofs" for vz) mountType: null # Enable inotify support for mounted directories (EXPERIMENTAL) diff --git a/website/content/en/docs/config/mount.md b/website/content/en/docs/config/mount.md index dd76e6e5695..4478db26f41 100644 --- a/website/content/en/docs/config/mount.md +++ b/website/content/en/docs/config/mount.md @@ -12,7 +12,7 @@ The default mount type is shown in the following table: | < 0.10 | reverse-sshfs + Builtin SFTP server | | >= 0.10 | reverse-sshfs + OpenSSH SFTP server | | >= 0.17 | reverse-sshfs + OpenSSH SFTP server for QEMU, virtiofs for VZ | -| >= 1.0 | 9p for QEMU, virtiofs for VZ | +| >= 1.0 | 9p for QEMU (on non-Windows), virtiofs for VZ | ## Mount types From cd7086ad3ecf14661b3e92b2a9d9a71c5860e90e Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 16:23:45 +0900 Subject: [PATCH 54/91] templates: default: use Ubuntu 25.04 (enables 9p again) Ubuntu 24.10 will reach EOL soon (2025-07-10). It should be still noted that Intel Mac with macOS prior to 15.5 requires setting `vmType` to `qemu` (issue 3334) Signed-off-by: Akihiro Suda --- .github/workflows/test.yml | 1 - templates/_images/ubuntu.yaml | 2 +- templates/default.yaml | 4 +--- templates/ubuntu.yaml | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3670fd353e..efbb33ba874 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -305,7 +305,6 @@ jobs: matrix: # Most templates use 9p as the mount type template: - - ubuntu-25.04.yaml # The default version of Ubuntu is still 24.10 due to https://github.com/lima-vm/lima/issues/3334 - alpine.yaml - debian.yaml # reverse-sshfs - fedora.yaml # The default version of Fedora is still 41 due to https://github.com/lima-vm/lima/issues/3334 diff --git a/templates/_images/ubuntu.yaml b/templates/_images/ubuntu.yaml index 9e033b6dd81..3475af5cbe5 120000 --- a/templates/_images/ubuntu.yaml +++ b/templates/_images/ubuntu.yaml @@ -1 +1 @@ -ubuntu-24.10.yaml \ No newline at end of file +ubuntu-25.04.yaml \ No newline at end of file diff --git a/templates/default.yaml b/templates/default.yaml index 344072a6b7e..11a88640239 100644 --- a/templates/default.yaml +++ b/templates/default.yaml @@ -91,9 +91,7 @@ mounts: [] # The issue was fixed in Linux v6.12-rc5 (https://github.com/torvalds/linux/commit/be2ca38). # # 🟢 Builtin default: [] -# 🔵 This file: ["9p"] (as Ubuntu 24.10 uses kernel 6.11) -mountTypesUnsupported: -- "9p" +mountTypesUnsupported: null # Mount type for above mounts, such as "reverse-sshfs" (from sshocker), "9p" (QEMU’s virtio-9p-pci, aka virtfs), # or "virtiofs" (experimental on Linux; needs `vmType: vz` on macOS). diff --git a/templates/ubuntu.yaml b/templates/ubuntu.yaml index 9e033b6dd81..3475af5cbe5 120000 --- a/templates/ubuntu.yaml +++ b/templates/ubuntu.yaml @@ -1 +1 @@ -ubuntu-24.10.yaml \ No newline at end of file +ubuntu-25.04.yaml \ No newline at end of file From d4adb1f3419a51463ef7b52983b35eeabd302750 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 20 Jun 2025 16:26:00 +0900 Subject: [PATCH 55/91] templates: fedora: use Fedora 42 It should be still noted that Intel Mac with macOS prior to 15.5 requires setting `vmType` to `qemu` (issue 3334) Signed-off-by: Akihiro Suda --- .github/workflows/test.yml | 3 +-- templates/README.md | 4 ++-- templates/_images/fedora.yaml | 2 +- templates/fedora.yaml | 2 +- templates/podman-rootful.yaml | 2 +- templates/podman.yaml | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index efbb33ba874..bc77b2f3c91 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -307,8 +307,7 @@ jobs: template: - alpine.yaml - debian.yaml # reverse-sshfs - - fedora.yaml # The default version of Fedora is still 41 due to https://github.com/lima-vm/lima/issues/3334 - - fedora-42.yaml + - fedora.yaml - archlinux.yaml - opensuse.yaml - docker.yaml diff --git a/templates/README.md b/templates/README.md index ace299c54ff..f6fe4c65fbf 100644 --- a/templates/README.md +++ b/templates/README.md @@ -22,8 +22,8 @@ Distro: - [`centos-stream-10`](./centos-stream-10.yaml): CentOS Stream 10 - [`debian-11`](./debian-11.yaml): Debian GNU/Linux 11(bullseye) - [`debian-12`](./debian-12.yaml), `debian.yaml`: ⭐Debian GNU/Linux 12(bookworm) -- [`fedora-41`](./fedora-41.yaml), `fedora.yaml`: ⭐Fedora 41 -- [`fedora-42`](./fedora-42.yaml): Fedora 42 +- [`fedora-41`](./fedora-41.yaml): Fedora 41 +- [`fedora-42`](./fedora-42.yaml), `fedora.yaml`: ⭐Fedora 42 - [`opensuse-leap`](./opensuse-leap.yaml), `opensuse.yaml`: ⭐openSUSE Leap - [`oraclelinux-8`](./oraclelinux-8.yaml): Oracle Linux 8 - [`oraclelinux-9`](./oraclelinux-9.yaml), `oraclelinux.yaml`: Oracle Linux 9 diff --git a/templates/_images/fedora.yaml b/templates/_images/fedora.yaml index 98dcf55f942..660722ae4b6 120000 --- a/templates/_images/fedora.yaml +++ b/templates/_images/fedora.yaml @@ -1 +1 @@ -fedora-41.yaml \ No newline at end of file +fedora-42.yaml \ No newline at end of file diff --git a/templates/fedora.yaml b/templates/fedora.yaml index 98dcf55f942..660722ae4b6 120000 --- a/templates/fedora.yaml +++ b/templates/fedora.yaml @@ -1 +1 @@ -fedora-41.yaml \ No newline at end of file +fedora-42.yaml \ No newline at end of file diff --git a/templates/podman-rootful.yaml b/templates/podman-rootful.yaml index c0dcebe937a..ba85ce09cd2 100644 --- a/templates/podman-rootful.yaml +++ b/templates/podman-rootful.yaml @@ -13,7 +13,7 @@ minimumLimaVersion: 1.1.0 base: -- template://_images/fedora-41 +- template://_images/fedora - template://_default/mounts containerd: diff --git a/templates/podman.yaml b/templates/podman.yaml index e50cf370203..968a4646248 100644 --- a/templates/podman.yaml +++ b/templates/podman.yaml @@ -13,7 +13,7 @@ minimumLimaVersion: 1.1.0 base: -- template://_images/fedora-41 +- template://_images/fedora - template://_default/mounts containerd: From 9f2b750754d29981fb9541f568437e82c16fccc2 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 26 Jun 2025 14:24:42 +0900 Subject: [PATCH 56/91] hack/test-templates.sh: workaround for agetty segfault agetty segfaults on Ubuntu 25.04 (x86_64): (gdb) bt #0 __strncmp_evex () at ../sysdeps/x86_64/multiarch/strcmp-evex.S:316 #1 0x000061700a0d3ee3 in parse_args (argc=9, argv=0x7ffc93bd6298, op=0x7ffc93bd4080) at term-utils/agetty.c:939 #2 main (argc=9, argv=0x7ffc93bd6298) at term-utils/agetty.c:403 Should be fixed in Ubuntu 25.10 with util-linux >= 2.41 Signed-off-by: Akihiro Suda --- hack/test-templates.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hack/test-templates.sh b/hack/test-templates.sh index 9bba8ba3785..2935907c99e 100755 --- a/hack/test-templates.sh +++ b/hack/test-templates.sh @@ -252,6 +252,13 @@ limactl shell "$NAME" bash -c "echo 'foo \"bar\"'" if [[ -n ${CHECKS["systemd"]} ]]; then set -x + # agetty segfaults on Ubuntu 25.04 (x86_64) + # > __strncmp_evex () at ../sysdeps/x86_64/multiarch/strcmp-evex.S:316 + # Should be fixed in Ubuntu 25.10 with util-linux >= 2.41 + # https://github.com/lima-vm/lima/pull/3643#issuecomment-3006788732 + if limactl shell "$NAME" systemctl -q is-failed serial-getty@ttyS0.service; then + limactl shell "$NAME" sudo systemctl reset-failed serial-getty@ttyS0.service + fi if ! limactl shell "$NAME" systemctl is-system-running --wait; then ERROR '"systemctl is-system-running" failed' diagnose "$NAME" From 5276974d48f73b0304498d9fb9785128cf699614 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Fri, 27 Jun 2025 18:16:03 +0300 Subject: [PATCH 57/91] refactor: Limayaml validation functions Signed-off-by: Oleksandr Redko --- cmd/limactl/edit.go | 2 +- pkg/limayaml/load.go | 2 +- pkg/limayaml/validate.go | 52 +++++++++++++++++------------------ pkg/limayaml/validate_test.go | 4 +-- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index 9c57e18a8a0..6474d5e8bef 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -121,7 +121,7 @@ func editAction(cmd *cobra.Command, args []string) error { return saveRejectedYAML(yBytes, err) } - if err := limayaml.ValidateYAMLAgainstLatestConfig(yBytes, yContent); err != nil { + if err := limayaml.ValidateAgainstLatestConfig(yBytes, yContent); err != nil { return saveRejectedYAML(yBytes, err) } diff --git a/pkg/limayaml/load.go b/pkg/limayaml/load.go index 13f71e8d92e..3929d7bac8a 100644 --- a/pkg/limayaml/load.go +++ b/pkg/limayaml/load.go @@ -63,7 +63,7 @@ func load(b []byte, filePath string, warn bool) (*LimaYAML, error) { } // It should be called before the `y` parameter is passed to FillDefault() that execute template. - if err := ValidateParamIsUsed(&y); err != nil { + if err := validateParamIsUsed(&y); err != nil { return nil, err } diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 443eaff02f7..8e349a2c199 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -29,28 +29,6 @@ import ( "github.com/lima-vm/lima/pkg/version/versionutil" ) -func validateFileObject(f File, fieldName string) error { - var errs error - if !strings.Contains(f.Location, "://") { - if _, err := localpathutil.Expand(f.Location); err != nil { - errs = errors.Join(errs, fmt.Errorf("field `%s.location` refers to an invalid local file path: %q: %w", fieldName, f.Location, err)) - } - // f.Location does NOT need to be accessible, so we do NOT check os.Stat(f.Location) - } - if !slices.Contains(ArchTypes, f.Arch) { - errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch)) - } - if f.Digest != "" { - if !f.Digest.Algorithm().Available() { - errs = errors.Join(errs, fmt.Errorf("field `%s.digest` refers to an unavailable digest algorithm", fieldName)) - } - if err := f.Digest.Validate(); err != nil { - errs = errors.Join(errs, fmt.Errorf("field `%s.digest` is invalid: %s: %w", fieldName, f.Digest.String(), err)) - } - } - return errs -} - func Validate(y *LimaYAML, warn bool) error { var errs error @@ -441,6 +419,28 @@ func Validate(y *LimaYAML, warn bool) error { return errs } +func validateFileObject(f File, fieldName string) error { + var errs error + if !strings.Contains(f.Location, "://") { + if _, err := localpathutil.Expand(f.Location); err != nil { + errs = errors.Join(errs, fmt.Errorf("field `%s.location` refers to an invalid local file path: %q: %w", fieldName, f.Location, err)) + } + // f.Location does NOT need to be accessible, so we do NOT check os.Stat(f.Location) + } + if !slices.Contains(ArchTypes, f.Arch) { + errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch)) + } + if f.Digest != "" { + if !f.Digest.Algorithm().Available() { + errs = errors.Join(errs, fmt.Errorf("field `%s.digest` refers to an unavailable digest algorithm", fieldName)) + } + if err := f.Digest.Validate(); err != nil { + errs = errors.Join(errs, fmt.Errorf("field `%s.digest` is invalid: %s: %w", fieldName, f.Digest.String(), err)) + } + } + return errs +} + func validateNetwork(y *LimaYAML) error { var errs error interfaceName := make(map[string]int) @@ -518,9 +518,9 @@ func validateNetwork(y *LimaYAML) error { return errs } -// ValidateParamIsUsed checks if the keys in the `param` field are used in any script, probe, copyToHost, or portForward. +// validateParamIsUsed checks if the keys in the `param` field are used in any script, probe, copyToHost, or portForward. // It should be called before the `y` parameter is passed to FillDefault() that execute template. -func ValidateParamIsUsed(y *LimaYAML) error { +func validateParamIsUsed(y *LimaYAML) error { for key := range y.Param { re, err := regexp.Compile(`{{[^}]*\.Param\.` + key + `[^}]*}}|\bPARAM_` + key + `\b`) if err != nil { @@ -611,9 +611,9 @@ func warnExperimental(y *LimaYAML) { } } -// ValidateYAMLAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML. +// ValidateAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML. // This validates configuration rules that disallow certain changes, such as shrinking the disk. -func ValidateYAMLAgainstLatestConfig(yNew, yLatest []byte) error { +func ValidateAgainstLatestConfig(yNew, yLatest []byte) error { var n LimaYAML // Load the latest YAML and fill in defaults diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index e136c6cbe06..6d45941707c 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -266,7 +266,7 @@ provision: "field `provision[1].path` must not be empty when mode is \"data\"") } -func TestValidateYAMLAgainstLatestConfig(t *testing.T) { +func TestValidateAgainstLatestConfig(t *testing.T) { tests := []struct { name string yNew string @@ -308,7 +308,7 @@ func TestValidateYAMLAgainstLatestConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := ValidateYAMLAgainstLatestConfig([]byte(tt.yNew), []byte(tt.yLatest)) + err := ValidateAgainstLatestConfig([]byte(tt.yNew), []byte(tt.yLatest)) if tt.wantErr == nil { assert.NilError(t, err) } else { From 45062c7f1c59a4276a139ed960e1c9f2f097967d Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Fri, 27 Jun 2025 13:35:56 -0700 Subject: [PATCH 58/91] Bump ltag to fix module name Signed-off-by: Jan Dubois --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc77b2f3c91..4e27bc4508b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,9 +63,7 @@ jobs: # the allow list corresponds to https://github.com/cncf/foundation/blob/e5db022a0009f4db52b89d9875640cf3137153fe/allowed-third-party-license-policy.md run: go-licenses check --include_tests ./... --allowed_licenses=$(cat ./hack/allowed-licenses.txt) - name: Install ltag - # The GitHub repo has been moved from kunalkushwaha/ltag to containerd/ltag, - # but the Go module name is not changed yet: https://github.com/containerd/ltag/issues/17 - run: go install github.com/kunalkushwaha/ltag@v0.2.5 + run: go install github.com/containerd/ltag@latest - name: Check license boilerplates run: ltag -t ./hack/ltag --check -v From 7ab928a0716afb2655b87844de62338993a8d22d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 01:34:16 +0000 Subject: [PATCH 59/91] build(deps): bump github/codeql-action from 3.29.0 to 3.29.1 --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yaml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 15d1155f7ac..d68ef2abf81 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Initialize CodeQL - uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/init@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -48,6 +48,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/analyze@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 49f7a95ea3f..d181a25cfe5 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 with: sarif_file: results.sarif From 4211491188f0a480793c1acaf6139de5d77ec47f Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 30 Jun 2025 16:25:37 +0900 Subject: [PATCH 60/91] docs: remove unnecessary references to macOS Signed-off-by: Akihiro Suda --- .github/workflows/release.yml | 6 +++--- README.md | 2 +- website/content/en/docs/config/disk.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cf4435da3c..510516ad824 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,12 +92,12 @@ jobs: ## Usage \`\`\`console - [macOS]$ limactl create - [macOS]$ limactl start + $ limactl create + $ limactl start ... INFO[0029] READY. Run \`lima\` to open the shell. - [macOS]$ lima uname + $ lima uname Linux \`\`\` diff --git a/README.md b/README.md index a264a19738a..fcf1b45e963 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ to Mac users, but Lima can be used for non-container applications as well. Lima also supports other container engines (Docker, Podman, Kubernetes, etc.) and non-macOS hosts (Linux, NetBSD, etc.). ## Getting started -Set up (on macOS): +Set up (Homebrew): ```bash brew install lima limactl start diff --git a/website/content/en/docs/config/disk.md b/website/content/en/docs/config/disk.md index db6879306fe..6f2207c83c5 100644 --- a/website/content/en/docs/config/disk.md +++ b/website/content/en/docs/config/disk.md @@ -2,7 +2,7 @@ title: Disks --- -This guide explains how to increase the disk size of a Lima VM running on macOS when you've run out of space, as well as how to edit the disk size using the `limactl` CLI. +This guide explains how to increase the disk size of a Lima VM when you've run out of space, as well as how to edit the disk size using the `limactl` CLI. ## Resize Disk Using limactl From 3db05ee82da42629fa2f74767ddde82c88114ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 30 Jun 2025 17:20:31 +0200 Subject: [PATCH 61/91] Avoid segfault with unknown versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the version does not parse, then semver returns nil. It will still fail later on validation, but not crash... Signed-off-by: Anders F Björklund --- pkg/limayaml/validate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 8e349a2c199..9b959f25d1a 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -44,7 +44,7 @@ func Validate(y *LimaYAML, warn bool) error { if err != nil { errs = errors.Join(errs, fmt.Errorf("can't parse builtin Lima version %q: %w", version.Version, err)) } - if versionutil.GreaterThan(*y.MinimumLimaVersion, limaVersion.String()) { + if limaVersion != nil && versionutil.GreaterThan(*y.MinimumLimaVersion, limaVersion.String()) { errs = errors.Join(errs, fmt.Errorf("template requires Lima version %q; this is only %q", *y.MinimumLimaVersion, limaVersion.String())) } } From 6f2ed17f5313e171e7ca2122ea94ba7f34f402bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 04:45:10 +0000 Subject: [PATCH 62/91] build(deps): bump github/codeql-action from 3.29.1 to 3.29.2 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.1 to 3.29.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/39edc492dbe16b1465b0cafca41432d857bdb31a...181d5eefc20863364f96762470ba6f862bdef56b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yaml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index d68ef2abf81..e2af9dc7a66 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Initialize CodeQL - uses: github/codeql-action/init@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -48,6 +48,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d181a25cfe5..fe2a1e208d1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: sarif_file: results.sarif From 2dd3319be85f9fa8aedf77577248a68e54de981b Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Tue, 1 Jul 2025 00:17:17 -0700 Subject: [PATCH 63/91] Pin ltag version Signed-off-by: Jan Dubois --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e27bc4508b..72a95673df7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,7 @@ jobs: # the allow list corresponds to https://github.com/cncf/foundation/blob/e5db022a0009f4db52b89d9875640cf3137153fe/allowed-third-party-license-policy.md run: go-licenses check --include_tests ./... --allowed_licenses=$(cat ./hack/allowed-licenses.txt) - name: Install ltag - run: go install github.com/containerd/ltag@latest + run: go install github.com/containerd/ltag@v0.3.0 - name: Check license boilerplates run: ltag -t ./hack/ltag --check -v From ae948cbde7abcec8ca6a96bec87ccdb4e1923fce Mon Sep 17 00:00:00 2001 From: Praful Khanduri Date: Tue, 1 Jul 2025 21:03:02 +0530 Subject: [PATCH 64/91] defined a common interface for nativeimgutil & qemuimgutil Signed-off-by: Praful Khanduri --- cmd/limactl/disk.go | 17 +-- pkg/imgutil/manager.go | 23 +++ pkg/{ => imgutil}/nativeimgutil/fuzz_test.go | 2 +- .../nativeimgutil/nativeimgutil.go | 75 ++++++---- .../nativeimgutil/nativeimgutil_test.go | 14 +- pkg/imgutil/proxyimgutil/proxyimgutil.go | 75 ++++++++++ .../qemuimgutil/qemuimgutil.go} | 137 +++++++++++++----- .../qemuimgutil/qemuimgutil_test.go} | 8 +- pkg/instance/start.go | 11 +- pkg/qemu/qemu.go | 10 +- pkg/vz/disk.go | 12 +- pkg/vz/vm_darwin.go | 7 +- 12 files changed, 280 insertions(+), 111 deletions(-) create mode 100644 pkg/imgutil/manager.go rename pkg/{ => imgutil}/nativeimgutil/fuzz_test.go (89%) rename pkg/{ => imgutil}/nativeimgutil/nativeimgutil.go (72%) rename pkg/{ => imgutil}/nativeimgutil/nativeimgutil_test.go (89%) create mode 100644 pkg/imgutil/proxyimgutil/proxyimgutil.go rename pkg/{qemu/imgutil/imgutil.go => imgutil/qemuimgutil/qemuimgutil.go} (72%) rename pkg/{qemu/imgutil/imgutil_test.go => imgutil/qemuimgutil/qemuimgutil_test.go} (97%) diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go index cf95c0162cf..7f19b069c5d 100644 --- a/cmd/limactl/disk.go +++ b/cmd/limactl/disk.go @@ -18,8 +18,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/lima-vm/lima/pkg/nativeimgutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -113,11 +112,8 @@ func diskCreateAction(cmd *cobra.Command, args []string) error { // qemu may not be available, use it only if needed. dataDisk := filepath.Join(diskDir, filenames.DataDisk) - if format == "raw" { - err = nativeimgutil.CreateRawDisk(dataDisk, int(diskSize)) - } else { - err = imgutil.CreateDisk(dataDisk, format, int(diskSize)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.CreateDisk(dataDisk, diskSize) if err != nil { rerr := os.RemoveAll(diskDir) if rerr != nil { @@ -410,11 +406,8 @@ func diskResizeAction(cmd *cobra.Command, args []string) error { // qemu may not be available, use it only if needed. dataDisk := filepath.Join(disk.Dir, filenames.DataDisk) - if disk.Format == "raw" { - err = nativeimgutil.ResizeRawDisk(dataDisk, int(diskSize)) - } else { - err = imgutil.ResizeDisk(dataDisk, disk.Format, int(diskSize)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.ResizeDisk(dataDisk, diskSize) if err != nil { return fmt.Errorf("failed to resize disk %q: %w", diskName, err) } diff --git a/pkg/imgutil/manager.go b/pkg/imgutil/manager.go new file mode 100644 index 00000000000..a7f16ad7776 --- /dev/null +++ b/pkg/imgutil/manager.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package imgutil + +import ( + "os" +) + +// ImageDiskManager defines the common operations for disk image utilities. +type ImageDiskManager interface { + // CreateDisk creates a new disk image with the specified size. + CreateDisk(disk string, size int64) error + + // ResizeDisk resizes an existing disk image to the specified size. + ResizeDisk(disk string, size int64) error + + // ConvertToRaw converts a disk image to raw format. + ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error + + // MakeSparse makes a file sparse, starting from the specified offset. + MakeSparse(f *os.File, offset int64) error +} diff --git a/pkg/nativeimgutil/fuzz_test.go b/pkg/imgutil/nativeimgutil/fuzz_test.go similarity index 89% rename from pkg/nativeimgutil/fuzz_test.go rename to pkg/imgutil/nativeimgutil/fuzz_test.go index 7a3face5a24..204d5583fdc 100644 --- a/pkg/nativeimgutil/fuzz_test.go +++ b/pkg/imgutil/nativeimgutil/fuzz_test.go @@ -17,6 +17,6 @@ func FuzzConvertToRaw(f *testing.F) { destPath := filepath.Join(t.TempDir(), "dest.img") err := os.WriteFile(srcPath, imgData, 0o600) assert.NilError(t, err) - _ = ConvertToRaw(srcPath, destPath, &size, withBacking) + _ = convertToRaw(srcPath, destPath, &size, withBacking) }) } diff --git a/pkg/nativeimgutil/nativeimgutil.go b/pkg/imgutil/nativeimgutil/nativeimgutil.go similarity index 72% rename from pkg/nativeimgutil/nativeimgutil.go rename to pkg/imgutil/nativeimgutil/nativeimgutil.go index b4ccb0eab0e..910512d9a3d 100644 --- a/pkg/nativeimgutil/nativeimgutil.go +++ b/pkg/imgutil/nativeimgutil/nativeimgutil.go @@ -28,36 +28,19 @@ import ( // aligned to 512 bytes. const sectorSize = 512 -// RoundUp rounds size up to sectorSize. -func RoundUp(size int) int { +// NativeImageUtil is the native implementation of the imgutil.ImageDiskManager. +type NativeImageUtil struct{} + +// roundUp rounds size up to sectorSize. +func roundUp(size int64) int64 { sectors := (size + sectorSize - 1) / sectorSize return sectors * sectorSize } -// CreateRawDisk creates an empty raw data disk. -func CreateRawDisk(disk string, size int) error { - if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { - return err - } - f, err := os.Create(disk) - if err != nil { - return err - } - defer f.Close() - roundedSize := RoundUp(size) - return f.Truncate(int64(roundedSize)) -} - -// ResizeRawDisk resizes a raw data disk. -func ResizeRawDisk(disk string, size int) error { - roundedSize := RoundUp(size) - return os.Truncate(disk, int64(roundedSize)) -} - -// ConvertToRaw converts a source disk into a raw disk. +// convertToRaw converts a source disk into a raw disk. // source and dest may be same. -// ConvertToRaw is a NOP if source == dest, and no resizing is needed. -func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { +// convertToRaw is a NOP if source == dest, and no resizing is needed. +func convertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { srcF, err := os.Open(source) if err != nil { return err @@ -106,7 +89,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b // Truncating before copy eliminates the seeks during copy and provide a // hint to the file system that may minimize allocations and fragmentation // of the file. - if err := MakeSparse(destTmpF, srcImg.Size()); err != nil { + if err := makeSparse(destTmpF, srcImg.Size()); err != nil { return err } @@ -125,7 +108,7 @@ func ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile b // Resize if size != nil { logrus.Infof("Expanding to %s", units.BytesSize(float64(*size))) - if err = MakeSparse(destTmpF, *size); err != nil { + if err = makeSparse(destTmpF, *size); err != nil { return err } } @@ -153,7 +136,7 @@ func convertRawToRaw(source, dest string, size *int64) error { if err != nil { return err } - if err = MakeSparse(destF, *size); err != nil { + if err = makeSparse(destF, *size); err != nil { _ = destF.Close() return err } @@ -162,9 +145,39 @@ func convertRawToRaw(source, dest string, size *int64) error { return nil } -func MakeSparse(f *os.File, n int64) error { - if _, err := f.Seek(n, io.SeekStart); err != nil { +func makeSparse(f *os.File, offset int64) error { + if _, err := f.Seek(offset, io.SeekStart); err != nil { return err } - return f.Truncate(n) + return f.Truncate(offset) +} + +// CreateDisk creates a new disk image with the specified size. +func (n *NativeImageUtil) CreateDisk(disk string, size int64) error { + if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { + return err + } + f, err := os.Create(disk) + if err != nil { + return err + } + defer f.Close() + roundedSize := roundUp(size) + return f.Truncate(int64(roundedSize)) +} + +// ConvertToRaw converts a disk image to raw format. +func (n *NativeImageUtil) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + return convertToRaw(source, dest, size, allowSourceWithBackingFile) +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (n *NativeImageUtil) ResizeDisk(disk string, size int64) error { + roundedSize := roundUp(size) + return os.Truncate(disk, roundedSize) +} + +// MakeSparse makes a file sparse, starting from the specified offset. +func (n *NativeImageUtil) MakeSparse(f *os.File, offset int64) error { + return makeSparse(f, offset) } diff --git a/pkg/nativeimgutil/nativeimgutil_test.go b/pkg/imgutil/nativeimgutil/nativeimgutil_test.go similarity index 89% rename from pkg/nativeimgutil/nativeimgutil_test.go rename to pkg/imgutil/nativeimgutil/nativeimgutil_test.go index 943a8a8c92d..466edc626c9 100644 --- a/pkg/nativeimgutil/nativeimgutil_test.go +++ b/pkg/imgutil/nativeimgutil/nativeimgutil_test.go @@ -15,8 +15,8 @@ import ( func TestRoundUp(t *testing.T) { tests := []struct { - Size int - Rounded int + Size int64 + Rounded int64 }{ {0, 0}, {1, 512}, @@ -25,7 +25,7 @@ func TestRoundUp(t *testing.T) { {123456789, 123457024}, } for _, tc := range tests { - if RoundUp(tc.Size) != tc.Rounded { + if roundUp(tc.Size) != tc.Rounded { t.Errorf("expected %d, got %d", tc.Rounded, tc.Size) } } @@ -63,7 +63,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("qcow without backing file", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(qcowImage.Name(), resultImage, nil, false) + err = convertToRaw(qcowImage.Name(), resultImage, nil, false) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) @@ -71,7 +71,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("qcow with backing file", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(qcowImage.Name(), resultImage, nil, true) + err = convertToRaw(qcowImage.Name(), resultImage, nil, true) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) @@ -80,7 +80,7 @@ func TestConvertToRaw(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) size := int64(2_097_152) // 2mb - err = ConvertToRaw(qcowImage.Name(), resultImage, &size, false) + err = convertToRaw(qcowImage.Name(), resultImage, &size, false) assert.NilError(t, err) assertFileEquals(t, rawImageExtended.Name(), resultImage) }) @@ -88,7 +88,7 @@ func TestConvertToRaw(t *testing.T) { t.Run("raw", func(t *testing.T) { resultImage := filepath.Join(tmpDir, strings.ReplaceAll(strings.ReplaceAll(t.Name(), string(os.PathSeparator), "_"), "/", "_")) - err = ConvertToRaw(rawImage.Name(), resultImage, nil, false) + err = convertToRaw(rawImage.Name(), resultImage, nil, false) assert.NilError(t, err) assertFileEquals(t, rawImage.Name(), resultImage) }) diff --git a/pkg/imgutil/proxyimgutil/proxyimgutil.go b/pkg/imgutil/proxyimgutil/proxyimgutil.go new file mode 100644 index 00000000000..7ff88abcef0 --- /dev/null +++ b/pkg/imgutil/proxyimgutil/proxyimgutil.go @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package proxyimgutil + +import ( + "errors" + "os" + "os/exec" + + "github.com/lima-vm/lima/pkg/imgutil" + "github.com/lima-vm/lima/pkg/imgutil/nativeimgutil" + "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" +) + +// ImageDiskManager is a proxy implementation of imgutil.ImageDiskManager that uses both QEMU and native image utilities. +type ImageDiskManager struct { + qemu imgutil.ImageDiskManager + native imgutil.ImageDiskManager +} + +// NewDiskUtil returns a new instance of ImageDiskManager that uses both QEMU and native image utilities. +func NewDiskUtil() imgutil.ImageDiskManager { + return &ImageDiskManager{ + qemu: &qemuimgutil.QemuImageUtil{DefaultFormat: qemuimgutil.QemuImgFormat}, + native: &nativeimgutil.NativeImageUtil{}, + } +} + +// CreateDisk creates a new disk image with the specified size. +func (p *ImageDiskManager) CreateDisk(disk string, size int64) error { + err := p.qemu.CreateDisk(disk, size) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.CreateDisk(disk, size) + } + return err +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (p *ImageDiskManager) ResizeDisk(disk string, size int64) error { + err := p.qemu.ResizeDisk(disk, size) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.ResizeDisk(disk, size) + } + return err +} + +// ConvertToRaw converts a disk image to raw format. +func (p *ImageDiskManager) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + err := p.qemu.ConvertToRaw(source, dest, size, allowSourceWithBackingFile) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.ConvertToRaw(source, dest, size, allowSourceWithBackingFile) + } + return err +} + +func (p *ImageDiskManager) MakeSparse(f *os.File, offset int64) error { + err := p.qemu.MakeSparse(f, offset) + if err == nil { + return nil + } + if errors.Is(err, exec.ErrNotFound) { + return p.native.MakeSparse(f, offset) + } + return err +} diff --git a/pkg/qemu/imgutil/imgutil.go b/pkg/imgutil/qemuimgutil/qemuimgutil.go similarity index 72% rename from pkg/qemu/imgutil/imgutil.go rename to pkg/imgutil/qemuimgutil/qemuimgutil.go index 18512bf0bf7..fc065450c59 100644 --- a/pkg/qemu/imgutil/imgutil.go +++ b/pkg/imgutil/qemuimgutil/qemuimgutil.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package imgutil +package qemuimgutil import ( "bytes" @@ -16,6 +16,28 @@ import ( "github.com/sirupsen/logrus" ) +const QemuImgFormat = "qcow2" + +// QemuImageUtil is the QEMU implementation of the imgutil Interface. +type QemuImageUtil struct { + DefaultFormat string // Default disk format, e.g., "qcow2" +} + +// Info corresponds to the output of `qemu-img info --output=json FILE`. +type Info struct { + Filename string `json:"filename,omitempty"` // since QEMU 1.3 + Format string `json:"format,omitempty"` // since QEMU 1.3 + VSize int64 `json:"virtual-size,omitempty"` // since QEMU 1.3 + ActualSize int64 `json:"actual-size,omitempty"` // since QEMU 1.3 + DirtyFlag bool `json:"dirty-flag,omitempty"` // since QEMU 5.2 + ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.3 + BackingFilename string `json:"backing-filename,omitempty"` // since QEMU 1.3 + FullBackingFilename string `json:"full-backing-filename,omitempty"` // since QEMU 1.3 + BackingFilenameFormat string `json:"backing-filename-format,omitempty"` // since QEMU 1.3 + FormatSpecific *InfoFormatSpecific `json:"format-specific,omitempty"` // since QEMU 1.7 + Children []InfoChild `json:"children,omitempty"` // since QEMU 8.0 +} + type InfoChild struct { Name string `json:"name,omitempty"` // since QEMU 8.0 Info Info `json:"info,omitempty"` // since QEMU 8.0 @@ -26,22 +48,8 @@ type InfoFormatSpecific struct { Data json.RawMessage `json:"data,omitempty"` // since QEMU 1.7 } -func CreateDisk(disk, format string, size int) error { - if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { - // disk already exists - return err - } - - args := []string{"create", "-f", format, disk, strconv.Itoa(size)} - cmd := exec.Command("qemu-img", args...) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) - } - return nil -} - -func ResizeDisk(disk, format string, size int) error { - args := []string{"resize", "-f", format, disk, strconv.Itoa(size)} +func resizeDisk(disk, format string, size int64) error { + args := []string{"resize", "-f", format, disk, strconv.FormatInt(size, 10)} cmd := exec.Command("qemu-img", args...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) @@ -94,22 +102,7 @@ type InfoFormatSpecificDataVmdkExtent struct { ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.7 } -// Info corresponds to the output of `qemu-img info --output=json FILE`. -type Info struct { - Filename string `json:"filename,omitempty"` // since QEMU 1.3 - Format string `json:"format,omitempty"` // since QEMU 1.3 - VSize int64 `json:"virtual-size,omitempty"` // since QEMU 1.3 - ActualSize int64 `json:"actual-size,omitempty"` // since QEMU 1.3 - DirtyFlag bool `json:"dirty-flag,omitempty"` // since QEMU 5.2 - ClusterSize int `json:"cluster-size,omitempty"` // since QEMU 1.3 - BackingFilename string `json:"backing-filename,omitempty"` // since QEMU 1.3 - FullBackingFilename string `json:"full-backing-filename,omitempty"` // since QEMU 1.3 - BackingFilenameFormat string `json:"backing-filename-format,omitempty"` // since QEMU 1.3 - FormatSpecific *InfoFormatSpecific `json:"format-specific,omitempty"` // since QEMU 1.7 - Children []InfoChild `json:"children,omitempty"` // since QEMU 8.0 -} - -func ConvertToRaw(source, dest string) error { +func convertToRaw(source, dest string) error { var stdout, stderr bytes.Buffer cmd := exec.Command("qemu-img", "convert", "-O", "raw", source, dest) cmd.Stdout = &stdout @@ -121,7 +114,7 @@ func ConvertToRaw(source, dest string) error { return nil } -func ParseInfo(b []byte) (*Info, error) { +func parseInfo(b []byte) (*Info, error) { var imgInfo Info if err := json.Unmarshal(b, &imgInfo); err != nil { return nil, err @@ -129,7 +122,7 @@ func ParseInfo(b []byte) (*Info, error) { return &imgInfo, nil } -func GetInfo(f string) (*Info, error) { +func getInfo(f string) (*Info, error) { var stdout, stderr bytes.Buffer cmd := exec.Command("qemu-img", "info", "--output=json", "--force-share", f) cmd.Stdout = &stdout @@ -138,10 +131,80 @@ func GetInfo(f string) (*Info, error) { return nil, fmt.Errorf("failed to run %v: stdout=%q, stderr=%q: %w", cmd.Args, stdout.String(), stderr.String(), err) } - return ParseInfo(stdout.Bytes()) + return parseInfo(stdout.Bytes()) +} + +// CreateDisk creates a new disk image with the specified size. +func (q *QemuImageUtil) CreateDisk(disk string, size int64) error { + if _, err := os.Stat(disk); err == nil || !errors.Is(err, fs.ErrNotExist) { + // disk already exists + return err + } + + args := []string{"create", "-f", q.DefaultFormat, disk, strconv.FormatInt(size, 10)} + cmd := exec.Command("qemu-img", args...) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err) + } + return nil +} + +// ResizeDisk resizes an existing disk image to the specified size. +func (q *QemuImageUtil) ResizeDisk(disk string, size int64) error { + info, err := getInfo(disk) + if err != nil { + return fmt.Errorf("failed to get info for disk %q: %w", disk, err) + } + return resizeDisk(disk, info.Format, size) +} + +// MakeSparse is a stub implementation as the qemu package doesn't provide this functionality. +func (q *QemuImageUtil) MakeSparse(_ *os.File, _ int64) error { + return nil +} + +// GetInfo retrieves the information of a disk image. +func GetInfo(path string) (*Info, error) { + qemuInfo, err := getInfo(path) + if err != nil { + return nil, err + } + + return qemuInfo, nil +} + +// ConvertToRaw converts a disk image to raw format. +func (q *QemuImageUtil) ConvertToRaw(source, dest string, size *int64, allowSourceWithBackingFile bool) error { + if !allowSourceWithBackingFile { + info, err := getInfo(source) + if err != nil { + return fmt.Errorf("failed to get info for source disk %q: %w", source, err) + } + if info.BackingFilename != "" || info.FullBackingFilename != "" { + return fmt.Errorf("qcow2 image %q has an unexpected backing file: %q", source, info.BackingFilename) + } + } + + if err := convertToRaw(source, dest); err != nil { + return err + } + + if size != nil { + destInfo, err := getInfo(dest) + if err != nil { + return fmt.Errorf("failed to get info for converted disk %q: %w", dest, err) + } + + if *size > destInfo.VSize { + return resizeDisk(dest, "raw", *size) + } + } + + return nil } -func AcceptableAsBasedisk(info *Info) error { +// AcceptableAsBaseDisk checks if a disk image is acceptable as a base disk. +func AcceptableAsBaseDisk(info *Info) error { switch info.Format { case "qcow2", "raw": // NOP diff --git a/pkg/qemu/imgutil/imgutil_test.go b/pkg/imgutil/qemuimgutil/qemuimgutil_test.go similarity index 97% rename from pkg/qemu/imgutil/imgutil_test.go rename to pkg/imgutil/qemuimgutil/qemuimgutil_test.go index bb630f48162..b0f39d43b41 100644 --- a/pkg/qemu/imgutil/imgutil_test.go +++ b/pkg/imgutil/qemuimgutil/qemuimgutil_test.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright The Lima Authors // SPDX-License-Identifier: Apache-2.0 -package imgutil +package qemuimgutil import ( "testing" @@ -52,7 +52,7 @@ func TestParseInfo(t *testing.T) { "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 1, len(info.Children)) assert.Check(t, info.FormatSpecific != nil) @@ -104,7 +104,7 @@ func TestParseInfo(t *testing.T) { "backing-filename": "foo.qcow2", "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 1, len(info.Children)) assert.Equal(t, "foo.qcow2", info.BackingFilename) @@ -202,7 +202,7 @@ func TestParseInfo(t *testing.T) { }, "dirty-flag": false }` - info, err := ParseInfo([]byte(s)) + info, err := parseInfo([]byte(s)) assert.NilError(t, err) assert.Equal(t, 3, len(info.Children)) assert.Equal(t, "foo.vmdk", info.Filename) diff --git a/pkg/instance/start.go b/pkg/instance/start.go index 1df00c785a3..acd3741e0bd 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -27,10 +27,9 @@ import ( "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/fileutils" hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/usrlocalsharelima" @@ -414,7 +413,6 @@ func prepareDiffDisk(inst *store.Instance) error { } diskSize := img.Size() - format := string(img.Type()) if inst.Disk == diskSize { return nil @@ -427,12 +425,9 @@ func prepareDiffDisk(inst *store.Instance) error { return errors.New("diffDisk: Shrinking is currently unavailable") } - if format == "raw" { - err = nativeimgutil.ResizeRawDisk(diffDisk, int(inst.Disk)) - } else { - err = imgutil.ResizeDisk(diffDisk, format, int(inst.Disk)) - } + diskUtil := proxyimgutil.NewDiskUtil() + err = diskUtil.ResizeDisk(diffDisk, inst.Disk) if err != nil { return err } diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go index 0ba01b616d5..8a54ba8a587 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/qemu/qemu.go @@ -28,12 +28,12 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/imgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -137,11 +137,11 @@ func EnsureDisk(ctx context.Context, cfg Config) error { if err != nil { return err } - baseDiskInfo, err := imgutil.GetInfo(baseDisk) + baseDiskInfo, err := qemuimgutil.GetInfo(baseDisk) if err != nil { return fmt.Errorf("failed to get the information of base disk %q: %w", baseDisk, err) } - if err = imgutil.AcceptableAsBasedisk(baseDiskInfo); err != nil { + if err = qemuimgutil.AcceptableAsBaseDisk(baseDiskInfo); err != nil { return fmt.Errorf("file %q is not acceptable as the base disk: %w", baseDisk, err) } if baseDiskInfo.Format == "" { @@ -691,11 +691,11 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er if diskSize, _ := units.RAMInBytes(*cfg.LimaYAML.Disk); diskSize > 0 { args = append(args, "-drive", fmt.Sprintf("file=%s,if=virtio,discard=on", diffDisk)) } else if !isBaseDiskCDROM { - baseDiskInfo, err := imgutil.GetInfo(baseDisk) + baseDiskInfo, err := qemuimgutil.GetInfo(baseDisk) if err != nil { return "", nil, fmt.Errorf("failed to get the information of %q: %w", baseDisk, err) } - if err = imgutil.AcceptableAsBasedisk(baseDiskInfo); err != nil { + if err = qemuimgutil.AcceptableAsBaseDisk(baseDiskInfo); err != nil { return "", nil, fmt.Errorf("file %q is not acceptable as the base disk: %w", baseDisk, err) } if baseDiskInfo.Format == "" { diff --git a/pkg/vz/disk.go b/pkg/vz/disk.go index 3fff17c48fd..06194faf5d2 100644 --- a/pkg/vz/disk.go +++ b/pkg/vz/disk.go @@ -14,8 +14,8 @@ import ( "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/iso9660util" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -26,6 +26,8 @@ func EnsureDisk(ctx context.Context, driver *driver.BaseDriver) error { return err } + diskUtil := proxyimgutil.NewDiskUtil() + baseDisk := filepath.Join(driver.Instance.Dir, filenames.BaseDisk) kernel := filepath.Join(driver.Instance.Dir, filenames.Kernel) kernelCmdline := filepath.Join(driver.Instance.Dir, filenames.KernelCmdline) @@ -78,13 +80,15 @@ func EnsureDisk(ctx context.Context, driver *driver.BaseDriver) error { if err != nil { return err } - if err = nativeimgutil.MakeSparse(diffDiskF, diskSize); err != nil { + + err = diskUtil.MakeSparse(diffDiskF, 0) + if err != nil { diffDiskF.Close() - return err + return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err) } return diffDiskF.Close() } - if err = nativeimgutil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { + if err = diskUtil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err) } return err diff --git a/pkg/vz/vm_darwin.go b/pkg/vz/vm_darwin.go index 1f21cf448b0..9ea33a8642b 100644 --- a/pkg/vz/vm_darwin.go +++ b/pkg/vz/vm_darwin.go @@ -25,9 +25,9 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/nativeimgutil" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" @@ -471,6 +471,8 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura } configurations = append(configurations, diffDisk) + diskUtil := proxyimgutil.NewDiskUtil() + for _, d := range driver.Instance.Config.AdditionalDisks { diskName := d.Name disk, err := store.InspectDisk(diskName) @@ -489,7 +491,8 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura extraDiskPath := filepath.Join(disk.Dir, filenames.DataDisk) // ConvertToRaw is a NOP if no conversion is needed logrus.Debugf("Converting extra disk %q to a raw disk (if it is not a raw)", extraDiskPath) - if err = nativeimgutil.ConvertToRaw(extraDiskPath, extraDiskPath, nil, true); err != nil { + + if err = diskUtil.ConvertToRaw(extraDiskPath, extraDiskPath, nil, true); err != nil { return fmt.Errorf("failed to convert extra disk %q to a raw disk: %w", extraDiskPath, err) } extraDiskPathAttachment, err := vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync(extraDiskPath, false, diskImageCachingMode, vz.DiskImageSynchronizationModeFsync) From f915900d382977f993f322405bea815820179693 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Mon, 30 Jun 2025 21:10:14 +0300 Subject: [PATCH 65/91] chore: Update golangci-lint from v2.1 to v2.2 Signed-off-by: Oleksandr Redko --- .github/workflows/test.yml | 2 +- .golangci.yml | 2 ++ cmd/limactl/tunnel.go | 12 ++++++------ pkg/guestagent/guestagent_linux.go | 2 +- pkg/guestagent/timesync/timesync_linux.go | 9 ++++----- pkg/hostagent/hostagent.go | 9 +++------ pkg/networks/commands_test.go | 4 ++-- pkg/portfwd/control_others.go | 2 +- pkg/portfwd/control_windows.go | 2 +- pkg/vz/network_darwin_test.go | 2 +- 10 files changed, 22 insertions(+), 24 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e27bc4508b..efa398bad3c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,7 +91,7 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 with: - version: v2.1 + version: v2.2 args: --verbose security: diff --git a/.golangci.yml b/.golangci.yml index 3634b2b9170..2c0329178d9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -81,6 +81,7 @@ linters: confidence: 0.6 # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md rules: + - name: bare-return - name: blank-imports - name: context-as-argument - name: context-keys-type @@ -106,6 +107,7 @@ linters: - name: superfluous-else - name: time-naming - name: unexported-return + - name: unnecessary-format - name: unreachable-code - name: unused-parameter - name: use-any diff --git a/cmd/limactl/tunnel.go b/cmd/limactl/tunnel.go index 451376175be..80c155546e6 100644 --- a/cmd/limactl/tunnel.go +++ b/cmd/limactl/tunnel.go @@ -118,14 +118,14 @@ func tunnelAction(cmd *cobra.Command, args []string) error { switch runtime.GOOS { case "darwin": - fmt.Fprintf(stdout, "Open (or whatever) →
,\n") - fmt.Fprintf(stdout, "and specify the following configuration:\n") - fmt.Fprintf(stdout, "- Server: 127.0.0.1\n") + fmt.Fprint(stdout, "Open (or whatever) →
,\n") + fmt.Fprint(stdout, "and specify the following configuration:\n") + fmt.Fprint(stdout, "- Server: 127.0.0.1\n") fmt.Fprintf(stdout, "- Port: %d\n", port) case "windows": - fmt.Fprintf(stdout, "Open ,\n") - fmt.Fprintf(stdout, "and specify the following configuration:\n") - fmt.Fprintf(stdout, "- Address: socks=127.0.0.1\n") + fmt.Fprint(stdout, "Open ,\n") + fmt.Fprint(stdout, "and specify the following configuration:\n") + fmt.Fprint(stdout, "- Address: socks=127.0.0.1\n") fmt.Fprintf(stdout, "- Port: %d\n", port) default: fmt.Fprintf(stdout, "Set `ALL_PROXY=socks5h://127.0.0.1:%d`, etc.\n", port) diff --git a/pkg/guestagent/guestagent_linux.go b/pkg/guestagent/guestagent_linux.go index 2013c61158a..40881adb860 100644 --- a/pkg/guestagent/guestagent_linux.go +++ b/pkg/guestagent/guestagent_linux.go @@ -169,7 +169,7 @@ func comparePorts(old, neww []*api.IPPort) (added, removed []*api.IPPort) { } } } - return + return added, removed } func (a *agent) collectEvent(ctx context.Context, st eventState) (*api.Event, eventState) { diff --git a/pkg/guestagent/timesync/timesync_linux.go b/pkg/guestagent/timesync/timesync_linux.go index af6130060ca..0a7c7c83fd7 100644 --- a/pkg/guestagent/timesync/timesync_linux.go +++ b/pkg/guestagent/timesync/timesync_linux.go @@ -18,18 +18,17 @@ func HasRTC() (bool, error) { return !errors.Is(err, os.ErrNotExist), err } -func GetRTCTime() (t time.Time, err error) { +func GetRTCTime() (time.Time, error) { f, err := os.Open(rtc) if err != nil { - return + return time.Time{}, err } defer f.Close() obj, err := unix.IoctlGetRTCTime(int(f.Fd())) if err != nil { - return + return time.Time{}, err } - t = time.Date(int(obj.Year+1900), time.Month(obj.Mon+1), int(obj.Mday), int(obj.Hour), int(obj.Min), int(obj.Sec), 0, time.UTC) - return t, nil + return time.Date(int(obj.Year+1900), time.Month(obj.Mon+1), int(obj.Mday), int(obj.Hour), int(obj.Min), int(obj.Sec), 0, time.UTC), nil } func SetSystemTime(t time.Time) error { diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index a6e293e11a2..88491d9babf 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -222,14 +222,11 @@ func writeSSHConfigFile(sshPath, instName, instDir, instSSHAddress string, sshLo if instDir == "" { return fmt.Errorf("directory is unknown for the instance %q", instName) } - var b bytes.Buffer - if _, err := fmt.Fprintf(&b, `# This SSH config file can be passed to 'ssh -F'. + b := bytes.NewBufferString(`# This SSH config file can be passed to 'ssh -F'. # This file is created by Lima, but not used by Lima itself currently. # Modifications to this file will be lost on restarting the Lima instance. -`); err != nil { - return err - } - if err := sshutil.Format(&b, sshPath, instName, sshutil.FormatConfig, +`) + if err := sshutil.Format(b, sshPath, instName, sshutil.FormatConfig, append(sshOpts, fmt.Sprintf("Hostname=%s", instSSHAddress), fmt.Sprintf("Port=%d", sshLocalPort), diff --git a/pkg/networks/commands_test.go b/pkg/networks/commands_test.go index 7da2bae627c..45bf0094ae6 100644 --- a/pkg/networks/commands_test.go +++ b/pkg/networks/commands_test.go @@ -49,7 +49,7 @@ func TestUser(t *testing.T) { t.Run("socket_vmnet", func(t *testing.T) { if ok, _ := config.IsDaemonInstalled(SocketVMNet); !ok { - t.Skipf("socket_vmnet is not installed") + t.Skip("socket_vmnet is not installed") } user, err := config.User(SocketVMNet) assert.NilError(t, err) @@ -80,7 +80,7 @@ func TestStartCmd(t *testing.T) { t.Run("socket_vmnet", func(t *testing.T) { if ok, _ := config.IsDaemonInstalled(SocketVMNet); !ok { - t.Skipf("socket_vmnet is not installed") + t.Skip("socket_vmnet is not installed") } cmd := config.StartCmd("shared", SocketVMNet) diff --git a/pkg/portfwd/control_others.go b/pkg/portfwd/control_others.go index d6e666acf4c..529b07503e6 100644 --- a/pkg/portfwd/control_others.go +++ b/pkg/portfwd/control_others.go @@ -26,5 +26,5 @@ func Control(_, _ string, c syscall.RawConn) (err error) { if controlErr != nil { err = controlErr } - return + return err } diff --git a/pkg/portfwd/control_windows.go b/pkg/portfwd/control_windows.go index db41ab11f7b..57b09cd9c34 100644 --- a/pkg/portfwd/control_windows.go +++ b/pkg/portfwd/control_windows.go @@ -19,5 +19,5 @@ func Control(_, _ string, c syscall.RawConn) (err error) { if controlErr != nil { err = controlErr } - return + return err } diff --git a/pkg/vz/network_darwin_test.go b/pkg/vz/network_darwin_test.go index f29cf85053b..8a6659f585e 100644 --- a/pkg/vz/network_darwin_test.go +++ b/pkg/vz/network_darwin_test.go @@ -81,7 +81,7 @@ func TestDialQemu(t *testing.T) { buf := make([]byte, vmnetMaxPacketSize) - t.Logf("Receiving and verifying data packets...") + t.Log("Receiving and verifying data packets...") for i := range packetsCount { n, err := vzConn.Read(buf) assert.NilError(t, err) From c57fbfb43bc4a365a1a73d00a3a5538425f6059f Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Wed, 2 Jul 2025 19:02:41 +0300 Subject: [PATCH 66/91] docs: fix the URL to faasd Signed-off-by: Oleksandr Redko --- templates/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/README.md b/templates/README.md index f6fe4c65fbf..04fed74e07f 100644 --- a/templates/README.md +++ b/templates/README.md @@ -54,7 +54,7 @@ Container image builders: - [`buildkit`](./buildkit.yaml): BuildKit Container orchestration: -- [`faasd`](./faasd.yaml): [Faasd](https://docs.openfaas.com/deployment/faasd/) +- [`faasd`](./faasd.yaml): [Faasd](https://docs.openfaas.com/deployment/edge/) - [`k3s`](./k3s.yaml): Kubernetes via k3s - [`k8s`](./k8s.yaml): Kubernetes via kubeadm - [`experimental/u7s`](./experimental/u7s.yaml): [Usernetes](https://github.com/rootless-containers/usernetes): Rootless Kubernetes From fda63b639973d2621ef0160d92da25ff20b8842d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Jul 2025 06:55:14 +0000 Subject: [PATCH 67/91] Document SSH config Include usage for easier Lima instance access Signed-off-by: Akihiro Suda --- website/content/en/docs/usage/_index.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/website/content/en/docs/usage/_index.md b/website/content/en/docs/usage/_index.md index 5a8667f95d9..7a77bb564c4 100644 --- a/website/content/en/docs/usage/_index.md +++ b/website/content/en/docs/usage/_index.md @@ -59,6 +59,19 @@ $ limactl ls --format='{{.SSHConfigFile}}' default $ ssh -F /Users/example/.lima/default/ssh.config lima-default ``` +#### Using SSH without the `-F` flag + +To connect directly without specifying the config file, add this to your `~/.ssh/config`: + +``` +Include ~/.lima/*/ssh.config +``` + +Then you can connect directly: +```bash +ssh lima-default +``` + ### Shell completion - To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`. - To enable zsh completion, see `limactl completion zsh --help` From 1d3d678475d5385b10c02e7c0316daaa59233c6f Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 3 Jul 2025 13:42:14 +0300 Subject: [PATCH 68/91] test: Add unit tests for Validate minimumLimaVersion in YAML Signed-off-by: Oleksandr Redko --- pkg/limayaml/validate_test.go | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go index 6d45941707c..e3cd4315562 100644 --- a/pkg/limayaml/validate_test.go +++ b/pkg/limayaml/validate_test.go @@ -8,6 +8,8 @@ import ( "testing" "gotest.tools/v3/assert" + + "github.com/lima-vm/lima/pkg/version" ) func TestValidateEmpty(t *testing.T) { @@ -17,6 +19,60 @@ func TestValidateEmpty(t *testing.T) { assert.Error(t, err, "field `images` must be set") } +func TestValidateMinimumLimaVersion(t *testing.T) { + images := `images: [{"location": "/"}]` + + tests := []struct { + name string + currentVersion string + minimumLimaVersion string + wantErr string + }{ + { + name: "minimumLimaVersion less than current version", + currentVersion: "1.1.1-114-g5bf5e513", + minimumLimaVersion: "1.1.0", + wantErr: "", + }, + { + name: "minimumLimaVersion greater than current version", + currentVersion: "1.1.1-114-g5bf5e513", + minimumLimaVersion: "1.1.2", + wantErr: `template requires Lima version "1.1.2"; this is only "1.1.1"`, + }, + { + name: "invalid current version", + currentVersion: "", + minimumLimaVersion: "0.8.0", + wantErr: `can't parse builtin Lima version "": is not in dotted-tri format`, + }, + { + name: "invalid minimumLimaVersion", + currentVersion: "1.1.1-114-g5bf5e513", + minimumLimaVersion: "invalid", + wantErr: "field `minimumLimaVersion` must be a semvar value, got \"invalid\": invalid is not in dotted-tri format\ntemplate requires Lima version \"invalid\"; this is only \"1.1.1\"", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + oldVersion := version.Version + version.Version = tt.currentVersion + t.Cleanup(func() { version.Version = oldVersion }) + + y, err := Load([]byte("minimumLimaVersion: "+tt.minimumLimaVersion+"\n"+images), "lima.yaml") + assert.NilError(t, err) + + err = Validate(y, false) + if tt.wantErr == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, tt.wantErr) + } + }) + } +} + func TestValidateProbes(t *testing.T) { images := `images: [{"location": "/"}]` validProbe := `probes: [{"script": "#!foo"}]` From 6473f5e1120d1aa73ebb5874839978b5ad34face Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 3 Jul 2025 16:22:43 +0600 Subject: [PATCH 69/91] Add timezone validation Signed-off-by: Dmitry Rubtsov --- pkg/limayaml/defaults.go | 52 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index d9d2c6229d3..cfa144c7e89 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -20,6 +20,7 @@ import ( "strings" "sync" "text/template" + "time" "github.com/coreos/go-semver/semver" "github.com/docker/go-units" @@ -137,21 +138,22 @@ func MACAddress(uniqueID string) string { func hostTimeZone() string { // WSL2 will automatically set the timezone - if runtime.GOOS != "windows" { - tz, err := os.ReadFile("/etc/timezone") - if err == nil { - return strings.TrimSpace(string(tz)) - } - zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime") - if err == nil { - for baseDir := filepath.Dir(zoneinfoFile); baseDir != "/"; baseDir = filepath.Dir(baseDir) { - if _, err = os.Stat(filepath.Join(baseDir, "Etc/UTC")); err == nil { - return strings.TrimPrefix(zoneinfoFile, baseDir+"/") - } - } - logrus.Warnf("could not locate zoneinfo directory from %q", zoneinfoFile) + if runtime.GOOS == "windows" { + return "" + } + + if tzBytes, err := os.ReadFile("/etc/timezone"); err == nil { + if tz := strings.TrimSpace(string(tzBytes)); isValidTimezone(tz) { + return tz } } + + if zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime"); err == nil { + if tz := extractTimezoneFromPath(zoneinfoFile); isValidTimezone(tz) { + return tz + } + } + return "" } @@ -1309,3 +1311,27 @@ func unique(s []string) []string { } return list } + +func isValidTimezone(tz string) bool { + if tz == "" { + return false + } + _, err := time.LoadLocation(tz) + if err != nil { + if len(tz) > 30 { + tz = tz[:30] + "..." + } + logrus.Warnf("invalid timezone %q", tz) + return false + } + return true +} + +func extractTimezoneFromPath(zoneinfoFile string) string { + for baseDir := filepath.Dir(zoneinfoFile); baseDir != "/"; baseDir = filepath.Dir(baseDir) { + if _, err := os.Stat(filepath.Join(baseDir, "Etc/UTC")); err == nil { + return strings.TrimPrefix(zoneinfoFile, baseDir+string(os.PathSeparator)) + } + } + return "" +} From e88664dd0477b4fcf66d0cd0981def7465e65adb Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 3 Jul 2025 13:54:21 +0300 Subject: [PATCH 70/91] chore: Enable unconvert linter Signed-off-by: Oleksandr Redko --- .golangci.yml | 1 + pkg/imgutil/nativeimgutil/nativeimgutil.go | 2 +- pkg/store/instance_windows.go | 9 ++++----- pkg/windows/process_windows.go | 2 +- pkg/wsl2/vm_windows.go | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2c0329178d9..9654f770a81 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,6 +25,7 @@ linters: - perfsprint - revive - staticcheck + - unconvert - unused - usetesting - whitespace diff --git a/pkg/imgutil/nativeimgutil/nativeimgutil.go b/pkg/imgutil/nativeimgutil/nativeimgutil.go index 910512d9a3d..3008ff376a0 100644 --- a/pkg/imgutil/nativeimgutil/nativeimgutil.go +++ b/pkg/imgutil/nativeimgutil/nativeimgutil.go @@ -163,7 +163,7 @@ func (n *NativeImageUtil) CreateDisk(disk string, size int64) error { } defer f.Close() roundedSize := roundUp(size) - return f.Truncate(int64(roundedSize)) + return f.Truncate(roundedSize) } // ConvertToRaw converts a disk image to raw format. diff --git a/pkg/store/instance_windows.go b/pkg/store/instance_windows.go index 4f7b317175a..084c44876ee 100644 --- a/pkg/store/instance_windows.go +++ b/pkg/store/instance_windows.go @@ -72,7 +72,7 @@ func GetWslStatus(instName string) (string, error) { "--verbose", }) if err != nil { - return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, string(out)) + return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, out) } if out == "" { @@ -80,9 +80,8 @@ func GetWslStatus(instName string) (string, error) { } // Check for edge cases first - outString := string(out) - if strings.Contains(outString, "Windows Subsystem for Linux has no installed distributions.") { - if strings.Contains(outString, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") { + if strings.Contains(out, "Windows Subsystem for Linux has no installed distributions.") { + if strings.Contains(out, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") { return StatusBroken, fmt.Errorf( "failed to read instance state for instance %q because no distro is installed,"+ "try running `wsl --install -d Ubuntu` and then re-running Lima", instName) @@ -96,7 +95,7 @@ func GetWslStatus(instName string) (string, error) { var instState string wslListColsRegex := regexp.MustCompile(`\s+`) // wsl --list --verbose may have different headers depending on localization, just split by line - for _, rows := range strings.Split(strings.ReplaceAll(string(out), "\r\n", "\n"), "\n") { + for _, rows := range strings.Split(strings.ReplaceAll(out, "\r\n", "\n"), "\n") { cols := wslListColsRegex.Split(strings.TrimSpace(rows), -1) nameIdx := 0 // '*' indicates default instance diff --git a/pkg/windows/process_windows.go b/pkg/windows/process_windows.go index 9c88bbaf36a..93cb760d8ad 100644 --- a/pkg/windows/process_windows.go +++ b/pkg/windows/process_windows.go @@ -29,7 +29,7 @@ func GetProcessCommandLine(name string) ([]string, error) { } var outJSON CommandLineJSON - if err = json.Unmarshal([]byte(out), &outJSON); err != nil { + if err = json.Unmarshal(out, &outJSON); err != nil { return nil, fmt.Errorf("failed to unmarshal %q as %T: %w", out, outJSON, err) } diff --git a/pkg/wsl2/vm_windows.go b/pkg/wsl2/vm_windows.go index 531de6c0116..0ee09bcae33 100644 --- a/pkg/wsl2/vm_windows.go +++ b/pkg/wsl2/vm_windows.go @@ -30,7 +30,7 @@ func startVM(ctx context.Context, distroName string) error { }, executil.WithContext(ctx)) if err != nil { return fmt.Errorf("failed to run `wsl.exe --distribution %s`: %w (out=%q)", - distroName, err, string(out)) + distroName, err, out) } return nil } @@ -48,7 +48,7 @@ func initVM(ctx context.Context, instanceDir, distroName string) error { }, executil.WithContext(ctx)) if err != nil { return fmt.Errorf("failed to run `wsl.exe --import %s %s %s`: %w (out=%q)", - distroName, instanceDir, baseDisk, err, string(out)) + distroName, instanceDir, baseDisk, err, out) } return nil } @@ -62,7 +62,7 @@ func stopVM(ctx context.Context, distroName string) error { }, executil.WithContext(ctx)) if err != nil { return fmt.Errorf("failed to run `wsl.exe --terminate %s`: %w (out=%q)", - distroName, err, string(out)) + distroName, err, out) } return nil } @@ -172,7 +172,7 @@ func unregisterVM(ctx context.Context, distroName string) error { }, executil.WithContext(ctx)) if err != nil { return fmt.Errorf("failed to run `wsl.exe --unregister %s`: %w (out=%q)", - distroName, err, string(out)) + distroName, err, out) } return nil } From cb3e378352b24ca84b35a38b8d6c05615cbd0516 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 3 Jul 2025 20:07:04 +0300 Subject: [PATCH 71/91] refactor: Avoid os.Getenv duplication Signed-off-by: Oleksandr Redko --- pkg/hostagent/hostagent.go | 2 +- pkg/qemu/qemu.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index 88491d9babf..c0452c8ba72 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -656,7 +656,7 @@ func (a *HostAgent) processGuestAgentEvents(ctx context.Context, client *guestag // - v1.1.0-beta.0: false useSSHFwd := false if envVar := os.Getenv("LIMA_SSH_PORT_FORWARDER"); envVar != "" { - b, err := strconv.ParseBool(os.Getenv("LIMA_SSH_PORT_FORWARDER")) + b, err := strconv.ParseBool(envVar) if err != nil { logrus.WithError(err).Warnf("invalid LIMA_SSH_PORT_FORWARDER value %q", envVar) } else { diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go index 8a54ba8a587..2fb08886ffc 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/qemu/qemu.go @@ -561,7 +561,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er var firmware string firmwareInBios := runtime.GOOS == "windows" if envVar := os.Getenv("_LIMA_QEMU_UEFI_IN_BIOS"); envVar != "" { - b, err := strconv.ParseBool(os.Getenv("_LIMA_QEMU_UEFI_IN_BIOS")) + b, err := strconv.ParseBool(envVar) if err != nil { logrus.WithError(err).Warnf("invalid _LIMA_QEMU_UEFI_IN_BIOS value %q", envVar) } else { From 380679c56865b8087865eb5161aca78cf71980b2 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 3 Jul 2025 19:47:16 +0600 Subject: [PATCH 72/91] Add tests for timezone validation, split platforms Signed-off-by: Dmitry Rubtsov --- pkg/limayaml/defaults.go | 46 ------------------ pkg/limayaml/defaults_unix.go | 58 +++++++++++++++++++++++ pkg/limayaml/defaults_unix_test.go | 75 ++++++++++++++++++++++++++++++ pkg/limayaml/defaults_windows.go | 11 +++++ 4 files changed, 144 insertions(+), 46 deletions(-) create mode 100644 pkg/limayaml/defaults_unix.go create mode 100644 pkg/limayaml/defaults_unix_test.go create mode 100644 pkg/limayaml/defaults_windows.go diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index cfa144c7e89..629cbcc3f97 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -20,7 +20,6 @@ import ( "strings" "sync" "text/template" - "time" "github.com/coreos/go-semver/semver" "github.com/docker/go-units" @@ -136,27 +135,6 @@ func MACAddress(uniqueID string) string { return hw.String() } -func hostTimeZone() string { - // WSL2 will automatically set the timezone - if runtime.GOOS == "windows" { - return "" - } - - if tzBytes, err := os.ReadFile("/etc/timezone"); err == nil { - if tz := strings.TrimSpace(string(tzBytes)); isValidTimezone(tz) { - return tz - } - } - - if zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime"); err == nil { - if tz := extractTimezoneFromPath(zoneinfoFile); isValidTimezone(tz) { - return tz - } - } - - return "" -} - func defaultCPUs() int { const x = 4 if hostCPUs := runtime.NumCPU(); hostCPUs < x { @@ -1311,27 +1289,3 @@ func unique(s []string) []string { } return list } - -func isValidTimezone(tz string) bool { - if tz == "" { - return false - } - _, err := time.LoadLocation(tz) - if err != nil { - if len(tz) > 30 { - tz = tz[:30] + "..." - } - logrus.Warnf("invalid timezone %q", tz) - return false - } - return true -} - -func extractTimezoneFromPath(zoneinfoFile string) string { - for baseDir := filepath.Dir(zoneinfoFile); baseDir != "/"; baseDir = filepath.Dir(baseDir) { - if _, err := os.Stat(filepath.Join(baseDir, "Etc/UTC")); err == nil { - return strings.TrimPrefix(zoneinfoFile, baseDir+string(os.PathSeparator)) - } - } - return "" -} diff --git a/pkg/limayaml/defaults_unix.go b/pkg/limayaml/defaults_unix.go new file mode 100644 index 00000000000..fdce36cbb56 --- /dev/null +++ b/pkg/limayaml/defaults_unix.go @@ -0,0 +1,58 @@ +//go:build !windows + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package limayaml + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/sirupsen/logrus" +) + +func hostTimeZone() string { + if tzBytes, err := os.ReadFile("/etc/timezone"); err == nil { + if tz := strings.TrimSpace(string(tzBytes)); tz != "" { + if _, err := time.LoadLocation(tz); err != nil { + logrus.Warnf("invalid timezone found in /etc/timezone: %v", err) + } else { + return tz + } + } + } + + if zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime"); err == nil { + if tz, err := extractTZFromPath(zoneinfoFile); err != nil { + logrus.Warnf("failed to extract timezone from %s: %v", zoneinfoFile, err) + } else { + return tz + } + } + + logrus.Warn("unable to determine host timezone, falling back to default value") + return "" +} + +func extractTZFromPath(zoneinfoFile string) (string, error) { + if zoneinfoFile == "" { + return "", errors.New("invalid zoneinfo file path") + } + + if _, err := os.Stat(zoneinfoFile); os.IsNotExist(err) { + return "", fmt.Errorf("zoneinfo file does not exist: %s", zoneinfoFile) + } + + for dir := filepath.Dir(zoneinfoFile); dir != filepath.Dir(dir); dir = filepath.Dir(dir) { + if _, err := os.Stat(filepath.Join(dir, "Etc", "UTC")); err == nil { + return filepath.Rel(dir, zoneinfoFile) + } + } + + return "", errors.New("timezone base directory not found") +} diff --git a/pkg/limayaml/defaults_unix_test.go b/pkg/limayaml/defaults_unix_test.go new file mode 100644 index 00000000000..26f33228489 --- /dev/null +++ b/pkg/limayaml/defaults_unix_test.go @@ -0,0 +1,75 @@ +//go:build !windows + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package limayaml + +import ( + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" +) + +func TestExtractTimezoneFromPath(t *testing.T) { + tmpDir := t.TempDir() + + // Create test timezone directory structure + assert.NilError(t, os.MkdirAll(filepath.Join(tmpDir, "Etc"), 0o755)) + assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "Etc", "UTC"), []byte{}, 0o644)) + assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "UTC"), []byte{}, 0o644)) + assert.NilError(t, os.MkdirAll(filepath.Join(tmpDir, "Antarctica"), 0o755)) + assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "Antarctica", "Troll"), []byte{}, 0o644)) + + tests := []struct { + name string + path string + want string + wantErr bool + }{ + { + "valid_timezone", + filepath.Join(tmpDir, "Antarctica", "Troll"), + "Antarctica/Troll", + false, + }, + { + "root_level_zone", + filepath.Join(tmpDir, "UTC"), + "UTC", + false, + }, + { + "outside_zoneinfo", + "/tmp/somefile", + "", + true, + }, + { + "empty_path", + "", + "", + true, + }, + { + "nonexistent_file", + filepath.Join(tmpDir, "Invalid", "Zone"), + "", + true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := extractTZFromPath(tt.path) + if tt.wantErr { + assert.Assert(t, err != nil, "expected error but got none") + } else { + assert.NilError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/limayaml/defaults_windows.go b/pkg/limayaml/defaults_windows.go new file mode 100644 index 00000000000..f2a43775361 --- /dev/null +++ b/pkg/limayaml/defaults_windows.go @@ -0,0 +1,11 @@ +//go:build windows + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package limayaml + +func hostTimeZone() string { + // WSL2 will automatically set the timezone + return "" +} From f885c17007baac280c5db859901d1c39a34aa0e3 Mon Sep 17 00:00:00 2001 From: Balaji Vijayakumar Date: Fri, 4 Jul 2025 10:11:54 +0530 Subject: [PATCH 73/91] Fix tunneling of connection when keepalive is set Signed-off-by: Balaji Vijayakumar --- pkg/portfwd/client.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/portfwd/client.go b/pkg/portfwd/client.go index d76bfe9e716..81c5c21e17b 100644 --- a/pkg/portfwd/client.go +++ b/pkg/portfwd/client.go @@ -10,9 +10,9 @@ import ( "time" "github.com/containers/gvisor-tap-vsock/pkg/services/forwarder" + "github.com/containers/gvisor-tap-vsock/pkg/tcpproxy" "github.com/sirupsen/logrus" - "github.com/lima-vm/lima/pkg/bicopy" "github.com/lima-vm/lima/pkg/guestagent/api" guestagentclient "github.com/lima-vm/lima/pkg/guestagent/api/client" ) @@ -33,7 +33,10 @@ func HandleTCPConnection(ctx context.Context, client *guestagentclient.GuestAgen } rw := &GrpcClientRW{stream: stream, id: id, addr: guestAddr, protocol: "tcp"} - bicopy.Bicopy(rw, conn, nil) + proxy := tcpproxy.DialProxy{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + return conn, nil + }} + proxy.HandleConn(rw) } func HandleUDPConnection(ctx context.Context, client *guestagentclient.GuestAgentClient, conn net.PacketConn, guestAddr string) { From 8b7ba1e2185cc6e8e9ecd8a3f207f0e3beb890f7 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Fri, 4 Jul 2025 19:34:43 +0300 Subject: [PATCH 74/91] refactor: Fix staticcheck QF* issues Signed-off-by: Oleksandr Redko --- .golangci.yml | 3 ++- .../kubernetesservice/kubernetesservice.go | 5 +++-- pkg/hostagent/dns/dns.go | 6 +----- pkg/hostagent/dns/dns_test.go | 20 +++++++++++++++++++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 9654f770a81..991e6939eec 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -118,7 +118,8 @@ linters: # https://staticcheck.dev/docs/configuration/options/#checks checks: - all - - -QF* + - -QF1001 # apply De Morgan's law + - -QF1008 # remove embedded field from selector - -SA3000 # false positive for Go 1.15+. See https://github.com/golang/go/issues/34129 - -ST1000 - -ST1001 # duplicates revive.dot-imports diff --git a/pkg/guestagent/kubernetesservice/kubernetesservice.go b/pkg/guestagent/kubernetesservice/kubernetesservice.go index 62668e31b04..843c0b3c382 100644 --- a/pkg/guestagent/kubernetesservice/kubernetesservice.go +++ b/pkg/guestagent/kubernetesservice/kubernetesservice.go @@ -140,9 +140,10 @@ func (s *ServiceWatcher) GetPorts() []Entry { } var port int32 - if service.Spec.Type == corev1.ServiceTypeNodePort { + switch service.Spec.Type { + case corev1.ServiceTypeNodePort: port = portEntry.NodePort - } else if service.Spec.Type == corev1.ServiceTypeLoadBalancer { + case corev1.ServiceTypeLoadBalancer: port = portEntry.Port } diff --git a/pkg/hostagent/dns/dns.go b/pkg/hostagent/dns/dns.go index de1e4fea261..d124e453145 100644 --- a/pkg/hostagent/dns/dns.go +++ b/pkg/hostagent/dns/dns.go @@ -78,11 +78,7 @@ func newStaticClientConfig(ips []string) (*dns.ClientConfig, error) { func (h *Handler) lookupCnameToHost(cname string) string { seen := make(map[string]bool) - for { - // break cyclic definition - if seen[cname] { - break - } + for !seen[cname] { // break cyclic definition if _, ok := h.cnameToHost[cname]; ok { seen[cname] = true cname = h.cnameToHost[cname] diff --git a/pkg/hostagent/dns/dns_test.go b/pkg/hostagent/dns/dns_test.go index c96449742e0..d21a06b5530 100644 --- a/pkg/hostagent/dns/dns_test.go +++ b/pkg/hostagent/dns/dns_test.go @@ -53,6 +53,9 @@ func TestDNSRecords(t *testing.T) { "host.lima.internal": "10.10.0.34", "my.host": "host.lima.internal", "default": "my.domain.com", + "cycle1.example.com": "cycle2.example.com", + "cycle2.example.com": "cycle1.example.com", + "self.example.com": "self.example.com", }, } @@ -120,6 +123,23 @@ func TestDNSRecords(t *testing.T) { assert.Assert(t, regexMatch(dnsResult.String(), tc.expectedCNAME)) } }) + + t.Run("test cyclic CNAME records", func(t *testing.T) { + tests := []struct { + testDomain string + expectedCNAME string + }{ + {testDomain: "cycle1.example.com", expectedCNAME: `cycle1.example.com.`}, + {testDomain: "self.example.com", expectedCNAME: `self.example.com.`}, + } + + for _, tc := range tests { + req := new(dns.Msg) + req.SetQuestion(dns.Fqdn(tc.testDomain), dns.TypeCNAME) + h.ServeDNS(w, req) + assert.Assert(t, regexMatch(dnsResult.String(), tc.expectedCNAME)) + } + }) } type TestResponseWriter struct{} From fadc39bcf763f8cd7bfedd21dbf34e3e748212f5 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sun, 6 Jul 2025 15:10:09 +0300 Subject: [PATCH 75/91] guestagent: Fix isEmptyEvent check The intention seems to be checking if an event is equal to empty event, ignoring the event time, by copying the event and setting its time to nil. However instead of copying the event, we use the same object and set the event time to nil. I don't know if this caused a user visible problem, but it likely log wrong event time if we log the event. Signed-off-by: Nir Soffer --- pkg/guestagent/guestagent_linux.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/guestagent/guestagent_linux.go b/pkg/guestagent/guestagent_linux.go index 40881adb860..0bcd49efa49 100644 --- a/pkg/guestagent/guestagent_linux.go +++ b/pkg/guestagent/guestagent_linux.go @@ -191,9 +191,8 @@ func (a *agent) collectEvent(ctx context.Context, st eventState) (*api.Event, ev func isEventEmpty(ev *api.Event) bool { empty := &api.Event{} - copied := ev - copied.Time = nil - return reflect.DeepEqual(empty, copied) + empty.Time = ev.Time + return reflect.DeepEqual(empty, ev) } func (a *agent) Events(ctx context.Context, ch chan *api.Event) { From 1f6fec2921964202aa5a51f54ada06c7146f044a Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sun, 6 Jul 2025 15:56:33 +0300 Subject: [PATCH 76/91] hostagent: Log ssh forwarding command We log a message when forwarding to guest unix socket, but it is not clear from the log how this is implemented, and the code is confusing (using ssh forwarding when using GRPC forwarding). Let's make this more clear by logging the ssh forwarding command. Example logs: {"level":"debug","msg":"Forwarding unix sockets","time":"2025-07-06T15:59:43+03:00"} {"level":"info","msg":"Forwarding \"/run/user/501/podman/podman.sock\" (guest) to \"/Users/nir/.lima/podman/sock/podman.sock\" (host)","time":"2025-07-06T15:59:43+03:00"} {"level":"debug","msg":"Running \"/usr/bin/ssh -F /dev/null -o IdentityFile=\\\"/Users/nir/.lima/_config/user\\\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o NoHostAuthenticationForLocalhost=yes -o PreferredAuthentications=publickey -o Compression=no -o BatchMode=yes -o IdentitiesOnly=yes -o GSSAPIAuthentication=no -o Ciphers=\\\"^aes128-gcm@openssh.com,aes256-gcm@openssh.com\\\" -o User=nir -o ControlMaster=auto -o ControlPath=\\\"/Users/nir/.lima/podman/ssh.sock\\\" -o ControlPersist=yes -T -O forward -L /Users/nir/.lima/podman/sock/podman.sock:/run/user/501/podman/podman.sock -N -f -p 53505 127.0.0.1 --\"","time":"2025-07-06T15:59:43+03:00"} Signed-off-by: Nir Soffer --- pkg/hostagent/hostagent.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index c0452c8ba72..908c8c6ac61 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -758,6 +758,7 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, } } cmd := exec.CommandContext(ctx, sshConfig.Binary(), args...) + logrus.Debugf("Running %q", cmd) if out, err := cmd.Output(); err != nil { if verb == verbForward && strings.HasPrefix(local, "/") { if reverse { From 5389e275efea93d75b49dbf95c21f040189bf317 Mon Sep 17 00:00:00 2001 From: Ansuman Sahoo Date: Fri, 23 May 2025 16:34:44 +0530 Subject: [PATCH 77/91] refactor(BaseDriver, drivers, image-downloader): remove driver.BaseDriver, move drivers and image-downloader to pkg/driver Signed-off-by: Ansuman Sahoo --- pkg/driver/driver.go | 87 +---------- .../qemu/entitlementutil/entitlementutil.go | 0 pkg/{ => driver}/qemu/qemu.go | 41 +----- pkg/{ => driver}/qemu/qemu_driver.go | 66 +++++++-- pkg/{ => driver}/qemu/qemu_test.go | 0 pkg/driver/vz/disk.go | 58 ++++++++ pkg/{ => driver}/vz/errors_darwin.go | 0 pkg/{ => driver}/vz/network_darwin.go | 0 pkg/{ => driver}/vz/network_darwin_test.go | 0 .../vz/rosetta_directory_share.go | 0 .../vz/rosetta_directory_share_arm64.go | 0 pkg/{ => driver}/vz/vm_darwin.go | 135 +++++++++--------- pkg/{ => driver}/vz/vz_driver_darwin.go | 71 +++++++-- pkg/driver/vz/vz_driver_others.go | 109 ++++++++++++++ pkg/{ => driver}/wsl2/fs.go | 12 +- pkg/{ => driver}/wsl2/lima-init.TEMPLATE | 0 pkg/{ => driver}/wsl2/vm_windows.go | 0 pkg/driver/wsl2/wsl_driver_others.go | 109 ++++++++++++++ pkg/{ => driver}/wsl2/wsl_driver_windows.go | 78 ++++++++-- pkg/driverutil/driverutil.go | 4 +- pkg/driverutil/instance.go | 17 +-- pkg/hostagent/hostagent.go | 10 +- pkg/imgutil/proxyimgutil/proxyimgutil.go | 2 +- pkg/instance/create.go | 5 +- pkg/instance/delete.go | 5 +- pkg/instance/start.go | 41 +++++- pkg/networks/usernet/client.go | 12 +- pkg/{imgutil => }/qemuimgutil/qemuimgutil.go | 0 .../qemuimgutil/qemuimgutil_test.go | 0 pkg/snapshot/snapshot.go | 17 +-- pkg/vz/disk.go | 95 ------------ pkg/vz/vz_driver_others.go | 43 ------ pkg/wsl2/wsl_driver_others.go | 43 ------ 33 files changed, 603 insertions(+), 457 deletions(-) rename pkg/{ => driver}/qemu/entitlementutil/entitlementutil.go (100%) rename pkg/{ => driver}/qemu/qemu.go (96%) rename pkg/{ => driver}/qemu/qemu_driver.go (91%) rename pkg/{ => driver}/qemu/qemu_test.go (100%) create mode 100644 pkg/driver/vz/disk.go rename pkg/{ => driver}/vz/errors_darwin.go (100%) rename pkg/{ => driver}/vz/network_darwin.go (100%) rename pkg/{ => driver}/vz/network_darwin_test.go (100%) rename pkg/{ => driver}/vz/rosetta_directory_share.go (100%) rename pkg/{ => driver}/vz/rosetta_directory_share_arm64.go (100%) rename pkg/{ => driver}/vz/vm_darwin.go (80%) rename pkg/{ => driver}/vz/vz_driver_darwin.go (79%) create mode 100644 pkg/driver/vz/vz_driver_others.go rename pkg/{ => driver}/wsl2/fs.go (65%) rename pkg/{ => driver}/wsl2/lima-init.TEMPLATE (100%) rename pkg/{ => driver}/wsl2/vm_windows.go (100%) create mode 100644 pkg/driver/wsl2/wsl_driver_others.go rename pkg/{ => driver}/wsl2/wsl_driver_windows.go (75%) rename pkg/{imgutil => }/qemuimgutil/qemuimgutil.go (100%) rename pkg/{imgutil => }/qemuimgutil/qemuimgutil_test.go (100%) delete mode 100644 pkg/vz/disk.go delete mode 100644 pkg/vz/vz_driver_others.go delete mode 100644 pkg/wsl2/wsl_driver_others.go diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index 457265aedd7..082f847207b 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -5,16 +5,10 @@ package driver import ( "context" - "errors" "net" - - "github.com/lima-vm/lima/pkg/store" ) // Driver interface is used by hostagent for managing vm. -// -// This interface is extended by BaseDriver which provides default implementation. -// All other driver definition must extend BaseDriver. type Driver interface { // Validate returns error if the current driver isn't support for given config Validate() error @@ -71,84 +65,7 @@ type Driver interface { // GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh). GuestAgentConn(_ context.Context) (net.Conn, error) -} - -type BaseDriver struct { - Instance *store.Instance - - SSHLocalPort int - VSockPort int - VirtioPort string -} - -var _ Driver = (*BaseDriver)(nil) - -func (d *BaseDriver) Validate() error { - return nil -} - -func (d *BaseDriver) Initialize(_ context.Context) error { - return nil -} - -func (d *BaseDriver) CreateDisk(_ context.Context) error { - return nil -} - -func (d *BaseDriver) Start(_ context.Context) (chan error, error) { - return nil, nil -} - -func (d *BaseDriver) CanRunGUI() bool { - return false -} - -func (d *BaseDriver) RunGUI() error { - return nil -} - -func (d *BaseDriver) Stop(_ context.Context) error { - return nil -} - -func (d *BaseDriver) Register(_ context.Context) error { - return nil -} - -func (d *BaseDriver) Unregister(_ context.Context) error { - return nil -} - -func (d *BaseDriver) ChangeDisplayPassword(_ context.Context, _ string) error { - return nil -} - -func (d *BaseDriver) GetDisplayConnection(_ context.Context) (string, error) { - return "", nil -} - -func (d *BaseDriver) CreateSnapshot(_ context.Context, _ string) error { - return errors.New("unimplemented") -} - -func (d *BaseDriver) ApplySnapshot(_ context.Context, _ string) error { - return errors.New("unimplemented") -} - -func (d *BaseDriver) DeleteSnapshot(_ context.Context, _ string) error { - return errors.New("unimplemented") -} - -func (d *BaseDriver) ListSnapshots(_ context.Context) (string, error) { - return "", errors.New("unimplemented") -} - -func (d *BaseDriver) ForwardGuestAgent() bool { - // if driver is not providing, use host agent - return d.VSockPort == 0 && d.VirtioPort == "" -} -func (d *BaseDriver) GuestAgentConn(_ context.Context) (net.Conn, error) { - // use the unix socket forwarded by host agent - return nil, nil + VSockPort() int + VirtioPort() string } diff --git a/pkg/qemu/entitlementutil/entitlementutil.go b/pkg/driver/qemu/entitlementutil/entitlementutil.go similarity index 100% rename from pkg/qemu/entitlementutil/entitlementutil.go rename to pkg/driver/qemu/entitlementutil/entitlementutil.go diff --git a/pkg/qemu/qemu.go b/pkg/driver/qemu/qemu.go similarity index 96% rename from pkg/qemu/qemu.go rename to pkg/driver/qemu/qemu.go index 2fb08886ffc..b1986d4358e 100644 --- a/pkg/qemu/qemu.go +++ b/pkg/driver/qemu/qemu.go @@ -28,12 +28,12 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/fileutils" - "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" + "github.com/lima-vm/lima/pkg/qemuimgutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) @@ -85,7 +85,7 @@ func minimumQemuVersion() (hardMin, softMin semver.Version) { } // EnsureDisk also ensures the kernel and the initrd. -func EnsureDisk(ctx context.Context, cfg Config) error { +func EnsureDisk(_ context.Context, cfg Config) error { diffDisk := filepath.Join(cfg.InstanceDir, filenames.DiffDisk) if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) { // disk is already ensured @@ -93,42 +93,7 @@ func EnsureDisk(ctx context.Context, cfg Config) error { } baseDisk := filepath.Join(cfg.InstanceDir, filenames.BaseDisk) - kernel := filepath.Join(cfg.InstanceDir, filenames.Kernel) - kernelCmdline := filepath.Join(cfg.InstanceDir, filenames.KernelCmdline) - initrd := filepath.Join(cfg.InstanceDir, filenames.Initrd) - if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) { - var ensuredBaseDisk bool - errs := make([]error, len(cfg.LimaYAML.Images)) - for i, f := range cfg.LimaYAML.Images { - if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *cfg.LimaYAML.Arch); err != nil { - errs[i] = err - continue - } - if f.Kernel != nil { - if _, err := fileutils.DownloadFile(ctx, kernel, f.Kernel.File, false, "the kernel", *cfg.LimaYAML.Arch); err != nil { - errs[i] = err - continue - } - if f.Kernel.Cmdline != "" { - if err := os.WriteFile(kernelCmdline, []byte(f.Kernel.Cmdline), 0o644); err != nil { - errs[i] = err - continue - } - } - } - if f.Initrd != nil { - if _, err := fileutils.DownloadFile(ctx, initrd, *f.Initrd, false, "the initrd", *cfg.LimaYAML.Arch); err != nil { - errs[i] = err - continue - } - } - ensuredBaseDisk = true - break - } - if !ensuredBaseDisk { - return fileutils.Errors(errs) - } - } + diskSize, _ := units.RAMInBytes(*cfg.LimaYAML.Disk) if diskSize == 0 { return nil diff --git a/pkg/qemu/qemu_driver.go b/pkg/driver/qemu/qemu_driver.go similarity index 91% rename from pkg/qemu/qemu_driver.go rename to pkg/driver/qemu/qemu_driver.go index e28b23429d5..084021b85f0 100644 --- a/pkg/qemu/qemu_driver.go +++ b/pkg/driver/qemu/qemu_driver.go @@ -27,33 +27,42 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/driver/qemu/entitlementutil" "github.com/lima-vm/lima/pkg/executil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks/usernet" "github.com/lima-vm/lima/pkg/osutil" - "github.com/lima-vm/lima/pkg/qemu/entitlementutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) type LimaQemuDriver struct { - *driver.BaseDriver + Instance *store.Instance + SSHLocalPort int + vSockPort int + virtioPort string + qCmd *exec.Cmd qWaitCh chan error vhostCmds []*exec.Cmd } -func New(driver *driver.BaseDriver) *LimaQemuDriver { - driver.VSockPort = 0 - driver.VirtioPort = filenames.VirtioPort +var _ driver.Driver = (*LimaQemuDriver)(nil) + +func New(inst *store.Instance, sshLocalPort int) *LimaQemuDriver { // virtserialport doesn't seem to work reliably: https://github.com/lima-vm/lima/issues/2064 // but on Windows default Unix socket forwarding is not available + var virtioPort string + virtioPort = filenames.VirtioPort if runtime.GOOS != "windows" { - driver.VirtioPort = "" + virtioPort = "" } return &LimaQemuDriver{ - BaseDriver: driver, + Instance: inst, + vSockPort: 0, + virtioPort: virtioPort, + SSHLocalPort: sshLocalPort, } } @@ -94,7 +103,7 @@ func (l *LimaQemuDriver) Start(ctx context.Context) (chan error, error) { LimaYAML: l.Instance.Config, SSHLocalPort: l.SSHLocalPort, SSHAddress: l.Instance.SSHAddress, - VirtioGA: l.VirtioPort != "", + VirtioGA: l.virtioPort != "", } qExe, qArgs, err := Cmdline(ctx, qCfg) if err != nil { @@ -212,7 +221,7 @@ func (l *LimaQemuDriver) Start(ctx context.Context) (chan error, error) { go func() { if usernetIndex := limayaml.FirstUsernetIndex(l.Instance.Config); usernetIndex != -1 { client := usernet.NewClientByName(l.Instance.Config.Networks[usernetIndex].Lima) - err := client.ConfigureDriver(ctx, l.BaseDriver) + err := client.ConfigureDriver(ctx, l.Instance, l.SSHLocalPort) if err != nil { l.qWaitCh <- err } @@ -260,11 +269,11 @@ func (l *LimaQemuDriver) checkBinarySignature() error { } // The codesign --xml option is only available on macOS Monterey and later if !macOSProductVersion.LessThan(*semver.New("12.0.0")) { - qExe, _, err := Exe(l.BaseDriver.Instance.Arch) + qExe, _, err := Exe(l.Instance.Arch) if err != nil { - return fmt.Errorf("failed to find the QEMU binary for the architecture %q: %w", l.BaseDriver.Instance.Arch, err) + return fmt.Errorf("failed to find the QEMU binary for the architecture %q: %w", l.Instance.Arch, err) } - if accel := Accel(l.BaseDriver.Instance.Arch); accel == "hvf" { + if accel := Accel(l.Instance.Arch); accel == "hvf" { entitlementutil.AskToSignIfNotSignedProperly(qExe) } } @@ -498,3 +507,36 @@ func (a *qArgTemplateApplier) applyTemplate(qArg string) (string, error) { } return b.String(), nil } + +func (l *LimaQemuDriver) Initialize(_ context.Context) error { + return nil +} + +func (l *LimaQemuDriver) CanRunGUI() bool { + return false +} + +func (l *LimaQemuDriver) RunGUI() error { + return nil +} + +func (l *LimaQemuDriver) Register(_ context.Context) error { + return nil +} + +func (l *LimaQemuDriver) Unregister(_ context.Context) error { + return nil +} + +func (l *LimaQemuDriver) ForwardGuestAgent() bool { + // if driver is not providing, use host agent + return l.vSockPort == 0 && l.virtioPort == "" +} + +func (l *LimaQemuDriver) VSockPort() int { + return l.vSockPort +} + +func (l *LimaQemuDriver) VirtioPort() string { + return l.virtioPort +} diff --git a/pkg/qemu/qemu_test.go b/pkg/driver/qemu/qemu_test.go similarity index 100% rename from pkg/qemu/qemu_test.go rename to pkg/driver/qemu/qemu_test.go diff --git a/pkg/driver/vz/disk.go b/pkg/driver/vz/disk.go new file mode 100644 index 00000000000..f98cf2290c8 --- /dev/null +++ b/pkg/driver/vz/disk.go @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package vz + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/docker/go-units" + + "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" + "github.com/lima-vm/lima/pkg/iso9660util" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" +) + +func EnsureDisk(_ context.Context, inst *store.Instance) error { + diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk) + if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) { + // disk is already ensured + return err + } + + diskUtil := proxyimgutil.NewDiskUtil() + + baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk) + + diskSize, _ := units.RAMInBytes(*inst.Config.Disk) + if diskSize == 0 { + return nil + } + isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk) + if err != nil { + return err + } + if isBaseDiskISO { + // Create an empty data volume (sparse) + diffDiskF, err := os.Create(diffDisk) + if err != nil { + return err + } + + err = diskUtil.MakeSparse(diffDiskF, 0) + if err != nil { + diffDiskF.Close() + return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err) + } + return diffDiskF.Close() + } + if err = diskUtil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { + return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err) + } + return err +} diff --git a/pkg/vz/errors_darwin.go b/pkg/driver/vz/errors_darwin.go similarity index 100% rename from pkg/vz/errors_darwin.go rename to pkg/driver/vz/errors_darwin.go diff --git a/pkg/vz/network_darwin.go b/pkg/driver/vz/network_darwin.go similarity index 100% rename from pkg/vz/network_darwin.go rename to pkg/driver/vz/network_darwin.go diff --git a/pkg/vz/network_darwin_test.go b/pkg/driver/vz/network_darwin_test.go similarity index 100% rename from pkg/vz/network_darwin_test.go rename to pkg/driver/vz/network_darwin_test.go diff --git a/pkg/vz/rosetta_directory_share.go b/pkg/driver/vz/rosetta_directory_share.go similarity index 100% rename from pkg/vz/rosetta_directory_share.go rename to pkg/driver/vz/rosetta_directory_share.go diff --git a/pkg/vz/rosetta_directory_share_arm64.go b/pkg/driver/vz/rosetta_directory_share_arm64.go similarity index 100% rename from pkg/vz/rosetta_directory_share_arm64.go rename to pkg/driver/vz/rosetta_directory_share_arm64.go diff --git a/pkg/vz/vm_darwin.go b/pkg/driver/vz/vm_darwin.go similarity index 80% rename from pkg/vz/vm_darwin.go rename to pkg/driver/vz/vm_darwin.go index 9ea33a8642b..147af63fddb 100644 --- a/pkg/vz/vm_darwin.go +++ b/pkg/driver/vz/vm_darwin.go @@ -24,7 +24,6 @@ import ( "github.com/lima-vm/go-qcow2reader/image/raw" "github.com/sirupsen/logrus" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" @@ -51,13 +50,13 @@ type virtualMachineWrapper struct { // Hold all *os.File created via socketpair() so that they won't get garbage collected. f.FD() gets invalid if f gets garbage collected. var vmNetworkFiles = make([]*os.File, 1) -func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWrapper, chan error, error) { - usernetClient, err := startUsernet(ctx, driver) +func startVM(ctx context.Context, inst *store.Instance, sshLocalPort int) (*virtualMachineWrapper, chan error, error) { + usernetClient, err := startUsernet(ctx, inst) if err != nil { return nil, nil, err } - machine, err := createVM(driver) + machine, err := createVM(inst) if err != nil { return nil, nil, err } @@ -95,7 +94,7 @@ func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWra case newState := <-machine.StateChangedNotify(): switch newState { case vz.VirtualMachineStateRunning: - pidFile := filepath.Join(driver.Instance.Dir, filenames.PIDFile(*driver.Instance.Config.VMType)) + pidFile := filepath.Join(inst.Dir, filenames.PIDFile(*inst.Config.VMType)) if _, err := os.Stat(pidFile); !errors.Is(err, os.ErrNotExist) { logrus.Errorf("pidfile %q already exists", pidFile) errCh <- err @@ -107,7 +106,7 @@ func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWra filesToRemove[pidFile] = struct{}{} logrus.Info("[VZ] - vm state change: running") - err := usernetClient.ConfigureDriver(ctx, driver) + err := usernetClient.ConfigureDriver(ctx, inst, sshLocalPort) if err != nil { errCh <- err } @@ -116,7 +115,7 @@ func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWra wrapper.mu.Lock() wrapper.stopped = true wrapper.mu.Unlock() - _ = usernetClient.UnExposeSSH(driver.SSHLocalPort) + _ = usernetClient.UnExposeSSH(inst.SSHLocalPort) errCh <- errors.New("vz driver state stopped") default: logrus.Debugf("[VZ] - vm state change: %q", newState) @@ -128,17 +127,17 @@ func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWra return wrapper, errCh, err } -func startUsernet(ctx context.Context, driver *driver.BaseDriver) (*usernet.Client, error) { - if firstUsernetIndex := limayaml.FirstUsernetIndex(driver.Instance.Config); firstUsernetIndex != -1 { - nwName := driver.Instance.Config.Networks[firstUsernetIndex].Lima +func startUsernet(ctx context.Context, inst *store.Instance) (*usernet.Client, error) { + if firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config); firstUsernetIndex != -1 { + nwName := inst.Config.Networks[firstUsernetIndex].Lima return usernet.NewClientByName(nwName), nil } // Start a in-process gvisor-tap-vsock - endpointSock, err := usernet.SockWithDirectory(driver.Instance.Dir, "", usernet.EndpointSock) + endpointSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.EndpointSock) if err != nil { return nil, err } - vzSock, err := usernet.SockWithDirectory(driver.Instance.Dir, "", usernet.FDSock) + vzSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock) if err != nil { return nil, err } @@ -150,7 +149,7 @@ func startUsernet(ctx context.Context, driver *driver.BaseDriver) (*usernet.Clie FdSocket: vzSock, Async: true, DefaultLeases: map[string]string{ - networks.SlirpIPAddress: limayaml.MACAddress(driver.Instance.Dir), + networks.SlirpIPAddress: limayaml.MACAddress(inst.Dir), }, Subnet: networks.SlirpNetwork, }) @@ -161,41 +160,41 @@ func startUsernet(ctx context.Context, driver *driver.BaseDriver) (*usernet.Clie return usernet.NewClient(endpointSock, subnetIP), err } -func createVM(driver *driver.BaseDriver) (*vz.VirtualMachine, error) { - vmConfig, err := createInitialConfig(driver) +func createVM(inst *store.Instance) (*vz.VirtualMachine, error) { + vmConfig, err := createInitialConfig(inst) if err != nil { return nil, err } - if err = attachPlatformConfig(driver, vmConfig); err != nil { + if err = attachPlatformConfig(inst, vmConfig); err != nil { return nil, err } - if err = attachSerialPort(driver, vmConfig); err != nil { + if err = attachSerialPort(inst, vmConfig); err != nil { return nil, err } - if err = attachNetwork(driver, vmConfig); err != nil { + if err = attachNetwork(inst, vmConfig); err != nil { return nil, err } - if err = attachDisks(driver, vmConfig); err != nil { + if err = attachDisks(inst, vmConfig); err != nil { return nil, err } - if err = attachDisplay(driver, vmConfig); err != nil { + if err = attachDisplay(inst, vmConfig); err != nil { return nil, err } - if err = attachFolderMounts(driver, vmConfig); err != nil { + if err = attachFolderMounts(inst, vmConfig); err != nil { return nil, err } - if err = attachAudio(driver, vmConfig); err != nil { + if err = attachAudio(inst, vmConfig); err != nil { return nil, err } - if err = attachOtherDevices(driver, vmConfig); err != nil { + if err = attachOtherDevices(inst, vmConfig); err != nil { return nil, err } @@ -207,20 +206,20 @@ func createVM(driver *driver.BaseDriver) (*vz.VirtualMachine, error) { return vz.NewVirtualMachine(vmConfig) } -func createInitialConfig(driver *driver.BaseDriver) (*vz.VirtualMachineConfiguration, error) { - bootLoader, err := bootLoader(driver) +func createInitialConfig(inst *store.Instance) (*vz.VirtualMachineConfiguration, error) { + bootLoader, err := bootLoader(inst) if err != nil { return nil, err } - bytes, err := units.RAMInBytes(*driver.Instance.Config.Memory) + bytes, err := units.RAMInBytes(*inst.Config.Memory) if err != nil { return nil, err } vmConfig, err := vz.NewVirtualMachineConfiguration( bootLoader, - uint(*driver.Instance.Config.CPUs), + uint(*inst.Config.CPUs), uint64(bytes), ) if err != nil { @@ -229,8 +228,8 @@ func createInitialConfig(driver *driver.BaseDriver) (*vz.VirtualMachineConfigura return vmConfig, nil } -func attachPlatformConfig(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { - machineIdentifier, err := getMachineIdentifier(driver) +func attachPlatformConfig(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { + machineIdentifier, err := getMachineIdentifier(inst) if err != nil { return err } @@ -241,7 +240,7 @@ func attachPlatformConfig(driver *driver.BaseDriver, vmConfig *vz.VirtualMachine } // nested virt - if *driver.Instance.Config.NestedVirtualization { + if *inst.Config.NestedVirtualization { macOSProductVersion, err := osutil.ProductVersion() if err != nil { return fmt.Errorf("failed to get macOS product version: %w", err) @@ -264,8 +263,8 @@ func attachPlatformConfig(driver *driver.BaseDriver, vmConfig *vz.VirtualMachine return nil } -func attachSerialPort(driver *driver.BaseDriver, config *vz.VirtualMachineConfiguration) error { - path := filepath.Join(driver.Instance.Dir, filenames.SerialVirtioLog) +func attachSerialPort(inst *store.Instance, config *vz.VirtualMachineConfiguration) error { + path := filepath.Join(inst.Dir, filenames.SerialVirtioLog) serialPortAttachment, err := vz.NewFileSerialPortAttachment(path, false) if err != nil { return err @@ -302,14 +301,14 @@ func newVirtioNetworkDeviceConfiguration(attachment vz.NetworkDeviceAttachment, return networkConfig, nil } -func attachNetwork(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { +func attachNetwork(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { var configurations []*vz.VirtioNetworkDeviceConfiguration - // Configure default usernetwork with limayaml.MACAddress(driver.Instance.Dir) for eth0 interface - firstUsernetIndex := limayaml.FirstUsernetIndex(driver.Instance.Config) + // Configure default usernetwork with limayaml.MACAddress(inst.Dir) for eth0 interface + firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config) if firstUsernetIndex == -1 { // slirp network using gvisor netstack - vzSock, err := usernet.SockWithDirectory(driver.Instance.Dir, "", usernet.FDSock) + vzSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock) if err != nil { return err } @@ -317,13 +316,13 @@ func attachNetwork(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigu if err != nil { return err } - networkConfig, err := newVirtioFileNetworkDeviceConfiguration(networkConn, limayaml.MACAddress(driver.Instance.Dir)) + networkConfig, err := newVirtioFileNetworkDeviceConfiguration(networkConn, limayaml.MACAddress(inst.Dir)) if err != nil { return err } configurations = append(configurations, networkConfig) } else { - vzSock, err := usernet.Sock(driver.Instance.Config.Networks[firstUsernetIndex].Lima, usernet.FDSock) + vzSock, err := usernet.Sock(inst.Config.Networks[firstUsernetIndex].Lima, usernet.FDSock) if err != nil { return err } @@ -331,14 +330,14 @@ func attachNetwork(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigu if err != nil { return err } - networkConfig, err := newVirtioFileNetworkDeviceConfiguration(networkConn, limayaml.MACAddress(driver.Instance.Dir)) + networkConfig, err := newVirtioFileNetworkDeviceConfiguration(networkConn, limayaml.MACAddress(inst.Dir)) if err != nil { return err } configurations = append(configurations, networkConfig) } - for i, nw := range driver.Instance.Networks { + for i, nw := range inst.Networks { if nw.VZNAT != nil && *nw.VZNAT { attachment, err := vz.NewNATNetworkDeviceAttachment() if err != nil { @@ -434,10 +433,10 @@ func validateDiskFormat(diskPath string) error { return nil } -func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { - baseDiskPath := filepath.Join(driver.Instance.Dir, filenames.BaseDisk) - diffDiskPath := filepath.Join(driver.Instance.Dir, filenames.DiffDisk) - ciDataPath := filepath.Join(driver.Instance.Dir, filenames.CIDataISO) +func attachDisks(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { + baseDiskPath := filepath.Join(inst.Dir, filenames.BaseDisk) + diffDiskPath := filepath.Join(inst.Dir, filenames.DiffDisk) + ciDataPath := filepath.Join(inst.Dir, filenames.CIDataISO) isBaseDiskCDROM, err := iso9660util.IsISO9660(baseDiskPath) if err != nil { return err @@ -473,7 +472,7 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura diskUtil := proxyimgutil.NewDiskUtil() - for _, d := range driver.Instance.Config.AdditionalDisks { + for _, d := range inst.Config.AdditionalDisks { diskName := d.Name disk, err := store.InspectDisk(diskName) if err != nil { @@ -484,7 +483,7 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura return fmt.Errorf("failed to run attach disk %q, in use by instance %q", diskName, disk.Instance) } logrus.Infof("Mounting disk %q on %q", diskName, disk.MountPoint) - err = disk.Lock(driver.Instance.Dir) + err = disk.Lock(inst.Dir) if err != nil { return fmt.Errorf("failed to run lock disk %q: %w", diskName, err) } @@ -523,8 +522,8 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura return nil } -func attachDisplay(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { - switch *driver.Instance.Config.Video.Display { +func attachDisplay(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { + switch *inst.Config.Video.Display { case "vz", "default": graphicsDeviceConfiguration, err := vz.NewVirtioGraphicsDeviceConfiguration() if err != nil { @@ -543,14 +542,14 @@ func attachDisplay(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigu case "none": return nil default: - return fmt.Errorf("unexpected video display %q", *driver.Instance.Config.Video.Display) + return fmt.Errorf("unexpected video display %q", *inst.Config.Video.Display) } } -func attachFolderMounts(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { +func attachFolderMounts(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { var mounts []vz.DirectorySharingDeviceConfiguration - if *driver.Instance.Config.MountType == limayaml.VIRTIOFS { - for i, mount := range driver.Instance.Config.Mounts { + if *inst.Config.MountType == limayaml.VIRTIOFS { + for i, mount := range inst.Config.Mounts { if _, err := os.Stat(mount.Location); errors.Is(err, os.ErrNotExist) { err := os.MkdirAll(mount.Location, 0o750) if err != nil { @@ -577,7 +576,7 @@ func attachFolderMounts(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineCo } } - if *driver.Instance.Config.Rosetta.Enabled { + if *inst.Config.Rosetta.Enabled { logrus.Info("Setting up Rosetta share") directorySharingDeviceConfig, err := createRosettaDirectoryShareConfiguration() if err != nil { @@ -593,8 +592,8 @@ func attachFolderMounts(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineCo return nil } -func attachAudio(driver *driver.BaseDriver, config *vz.VirtualMachineConfiguration) error { - switch *driver.Instance.Config.Audio.Device { +func attachAudio(inst *store.Instance, config *vz.VirtualMachineConfiguration) error { + switch *inst.Config.Audio.Device { case "vz", "default": outputStream, err := vz.NewVirtioSoundDeviceHostOutputStreamConfiguration() if err != nil { @@ -612,11 +611,11 @@ func attachAudio(driver *driver.BaseDriver, config *vz.VirtualMachineConfigurati case "", "none": return nil default: - return fmt.Errorf("unexpected audio device %q", *driver.Instance.Config.Audio.Device) + return fmt.Errorf("unexpected audio device %q", *inst.Config.Audio.Device) } } -func attachOtherDevices(_ *driver.BaseDriver, vmConfig *vz.VirtualMachineConfiguration) error { +func attachOtherDevices(_ *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error { entropyConfig, err := vz.NewVirtioEntropyDeviceConfiguration() if err != nil { return err @@ -690,8 +689,8 @@ func attachOtherDevices(_ *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigu return nil } -func getMachineIdentifier(driver *driver.BaseDriver) (*vz.GenericMachineIdentifier, error) { - identifier := filepath.Join(driver.Instance.Dir, filenames.VzIdentifier) +func getMachineIdentifier(inst *store.Instance) (*vz.GenericMachineIdentifier, error) { + identifier := filepath.Join(inst.Dir, filenames.VzIdentifier) if _, err := os.Stat(identifier); os.IsNotExist(err) { machineIdentifier, err := vz.NewGenericMachineIdentifier() if err != nil { @@ -706,15 +705,15 @@ func getMachineIdentifier(driver *driver.BaseDriver) (*vz.GenericMachineIdentifi return vz.NewGenericMachineIdentifierWithDataPath(identifier) } -func bootLoader(driver *driver.BaseDriver) (vz.BootLoader, error) { - linuxBootLoader, err := linuxBootLoader(driver) +func bootLoader(inst *store.Instance) (vz.BootLoader, error) { + linuxBootLoader, err := linuxBootLoader(inst) if linuxBootLoader != nil { return linuxBootLoader, nil } else if !errors.Is(err, os.ErrNotExist) { return nil, err } - efiVariableStore, err := getEFI(driver) + efiVariableStore, err := getEFI(inst) if err != nil { return nil, err } @@ -722,10 +721,10 @@ func bootLoader(driver *driver.BaseDriver) (vz.BootLoader, error) { return vz.NewEFIBootLoader(vz.WithEFIVariableStore(efiVariableStore)) } -func linuxBootLoader(driver *driver.BaseDriver) (*vz.LinuxBootLoader, error) { - kernel := filepath.Join(driver.Instance.Dir, filenames.Kernel) - kernelCmdline := filepath.Join(driver.Instance.Dir, filenames.KernelCmdline) - initrd := filepath.Join(driver.Instance.Dir, filenames.Initrd) +func linuxBootLoader(inst *store.Instance) (*vz.LinuxBootLoader, error) { + kernel := filepath.Join(inst.Dir, filenames.Kernel) + kernelCmdline := filepath.Join(inst.Dir, filenames.KernelCmdline) + initrd := filepath.Join(inst.Dir, filenames.Initrd) if _, err := os.Stat(kernel); err != nil { if errors.Is(err, os.ErrNotExist) { logrus.Debugf("Kernel file %q not found", kernel) @@ -747,8 +746,8 @@ func linuxBootLoader(driver *driver.BaseDriver) (*vz.LinuxBootLoader, error) { return vz.NewLinuxBootLoader(kernel, opt...) } -func getEFI(driver *driver.BaseDriver) (*vz.EFIVariableStore, error) { - efi := filepath.Join(driver.Instance.Dir, filenames.VzEfi) +func getEFI(inst *store.Instance) (*vz.EFIVariableStore, error) { + efi := filepath.Join(inst.Dir, filenames.VzEfi) if _, err := os.Stat(efi); os.IsNotExist(err) { return vz.NewEFIVariableStore(efi, vz.WithCreatingEFIVariableStore()) } diff --git a/pkg/vz/vz_driver_darwin.go b/pkg/driver/vz/vz_driver_darwin.go similarity index 79% rename from pkg/vz/vz_driver_darwin.go rename to pkg/driver/vz/vz_driver_darwin.go index ee1b34b910f..7242bf8bb34 100644 --- a/pkg/vz/vz_driver_darwin.go +++ b/pkg/driver/vz/vz_driver_darwin.go @@ -22,6 +22,7 @@ import ( "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/reflectutil" + "github.com/lima-vm/lima/pkg/store" ) var knownYamlProperties = []string{ @@ -68,16 +69,23 @@ var knownYamlProperties = []string{ const Enabled = true type LimaVzDriver struct { - *driver.BaseDriver + Instance *store.Instance + + SSHLocalPort int + vSockPort int + virtioPort string machine *virtualMachineWrapper } -func New(driver *driver.BaseDriver) *LimaVzDriver { - driver.VSockPort = 2222 - driver.VirtioPort = "" +var _ driver.Driver = (*LimaVzDriver)(nil) + +func New(inst *store.Instance, sshLocalPort int) *LimaVzDriver { return &LimaVzDriver{ - BaseDriver: driver, + Instance: inst, + vSockPort: 2222, + virtioPort: "", + SSHLocalPort: sshLocalPort, } } @@ -167,17 +175,17 @@ func (l *LimaVzDriver) Validate() error { } func (l *LimaVzDriver) Initialize(_ context.Context) error { - _, err := getMachineIdentifier(l.BaseDriver) + _, err := getMachineIdentifier(l.Instance) return err } func (l *LimaVzDriver) CreateDisk(ctx context.Context) error { - return EnsureDisk(ctx, l.BaseDriver) + return EnsureDisk(ctx, l.Instance) } func (l *LimaVzDriver) Start(ctx context.Context) (chan error, error) { logrus.Infof("Starting VZ (hint: to watch the boot progress, see %q)", filepath.Join(l.Instance.Dir, "serial*.log")) - vm, errCh, err := startVM(ctx, l.BaseDriver) + vm, errCh, err := startVM(ctx, l.Instance, l.SSHLocalPort) if err != nil { if errors.Is(err, vz.ErrUnsupportedOSVersion) { return nil, fmt.Errorf("vz driver requires macOS 13 or higher to run: %w", err) @@ -237,10 +245,55 @@ func (l *LimaVzDriver) Stop(_ context.Context) error { func (l *LimaVzDriver) GuestAgentConn(_ context.Context) (net.Conn, error) { for _, socket := range l.machine.SocketDevices() { - connect, err := socket.Connect(uint32(l.VSockPort)) + connect, err := socket.Connect(uint32(l.vSockPort)) if err == nil && connect.SourcePort() != 0 { return connect, nil } } return nil, errors.New("unable to connect to guest agent via vsock port 2222") } + +func (l *LimaVzDriver) Register(_ context.Context) error { + return nil +} + +func (l *LimaVzDriver) Unregister(_ context.Context) error { + return nil +} + +func (l *LimaVzDriver) ChangeDisplayPassword(_ context.Context, _ string) error { + return nil +} + +func (l *LimaVzDriver) GetDisplayConnection(_ context.Context) (string, error) { + return "", nil +} + +func (l *LimaVzDriver) CreateSnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaVzDriver) ApplySnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaVzDriver) DeleteSnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaVzDriver) ListSnapshots(_ context.Context) (string, error) { + return "", errors.New("unimplemented") +} + +func (l *LimaVzDriver) ForwardGuestAgent() bool { + // If driver is not providing, use host agent + return l.vSockPort == 0 && l.virtioPort == "" +} + +func (l *LimaVzDriver) VSockPort() int { + return l.vSockPort +} + +func (l *LimaVzDriver) VirtioPort() string { + return l.virtioPort +} diff --git a/pkg/driver/vz/vz_driver_others.go b/pkg/driver/vz/vz_driver_others.go new file mode 100644 index 00000000000..589d0e75486 --- /dev/null +++ b/pkg/driver/vz/vz_driver_others.go @@ -0,0 +1,109 @@ +//go:build !darwin || no_vz + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package vz + +import ( + "context" + "errors" + "net" + + "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/store" +) + +var ErrUnsupported = errors.New("vm driver 'vz' needs macOS 13 or later (Hint: try recompiling Lima if you are seeing this error on macOS 13)") + +const Enabled = false + +type LimaVzDriver struct { + Instance *store.Instance + + SSHLocalPort int + vSockPort int + virtioPort string +} + +var _ driver.Driver = (*LimaVzDriver)(nil) + +func New(_ *store.Instance, _ int) *LimaVzDriver { + return &LimaVzDriver{} +} + +func (l *LimaVzDriver) Validate() error { + return ErrUnsupported +} + +func (l *LimaVzDriver) Initialize(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) CreateDisk(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) Start(_ context.Context) (chan error, error) { + return nil, ErrUnsupported +} + +func (l *LimaVzDriver) Stop(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) CanRunGUI() bool { + return false +} + +func (l *LimaVzDriver) RunGUI() error { + return ErrUnsupported +} + +func (l *LimaVzDriver) ChangeDisplayPassword(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) GetDisplayConnection(_ context.Context) (string, error) { + return "", ErrUnsupported +} + +func (l *LimaVzDriver) CreateSnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) ApplySnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) DeleteSnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) ListSnapshots(_ context.Context) (string, error) { + return "", ErrUnsupported +} + +func (l *LimaVzDriver) Register(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) Unregister(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaVzDriver) ForwardGuestAgent() bool { + return false +} + +func (l *LimaVzDriver) GuestAgentConn(_ context.Context) (net.Conn, error) { + return nil, ErrUnsupported +} + +func (l *LimaVzDriver) VSockPort() int { + return l.vSockPort +} + +func (l *LimaVzDriver) VirtioPort() string { + return l.virtioPort +} diff --git a/pkg/wsl2/fs.go b/pkg/driver/wsl2/fs.go similarity index 65% rename from pkg/wsl2/fs.go rename to pkg/driver/wsl2/fs.go index 8a3060ac924..53823a7fb06 100644 --- a/pkg/wsl2/fs.go +++ b/pkg/driver/wsl2/fs.go @@ -11,19 +11,19 @@ import ( "github.com/sirupsen/logrus" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" ) // EnsureFs downloads the root fs. -func EnsureFs(ctx context.Context, driver *driver.BaseDriver) error { - baseDisk := filepath.Join(driver.Instance.Dir, filenames.BaseDisk) +func EnsureFs(ctx context.Context, inst *store.Instance) error { + baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk) if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) { var ensuredBaseDisk bool - errs := make([]error, len(driver.Instance.Config.Images)) - for i, f := range driver.Instance.Config.Images { - if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *driver.Instance.Config.Arch); err != nil { + errs := make([]error, len(inst.Config.Images)) + for i, f := range inst.Config.Images { + if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *inst.Config.Arch); err != nil { errs[i] = err continue } diff --git a/pkg/wsl2/lima-init.TEMPLATE b/pkg/driver/wsl2/lima-init.TEMPLATE similarity index 100% rename from pkg/wsl2/lima-init.TEMPLATE rename to pkg/driver/wsl2/lima-init.TEMPLATE diff --git a/pkg/wsl2/vm_windows.go b/pkg/driver/wsl2/vm_windows.go similarity index 100% rename from pkg/wsl2/vm_windows.go rename to pkg/driver/wsl2/vm_windows.go diff --git a/pkg/driver/wsl2/wsl_driver_others.go b/pkg/driver/wsl2/wsl_driver_others.go new file mode 100644 index 00000000000..274761b17ea --- /dev/null +++ b/pkg/driver/wsl2/wsl_driver_others.go @@ -0,0 +1,109 @@ +//go:build !windows || no_wsl + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package wsl2 + +import ( + "context" + "errors" + "net" + + "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/store" +) + +var ErrUnsupported = errors.New("vm driver 'wsl2' requires Windows 10 build 19041 or later (Hint: try recompiling Lima if you are seeing this error on Windows 10+)") + +const Enabled = false + +type LimaWslDriver struct { + Instance *store.Instance + + SSHLocalPort int + vSockPort int + virtioPort string +} + +var _ driver.Driver = (*LimaWslDriver)(nil) + +func New(_ *store.Instance, _ int) *LimaWslDriver { + return &LimaWslDriver{} +} + +func (l *LimaWslDriver) Validate() error { + return ErrUnsupported +} + +func (l *LimaWslDriver) Initialize(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) CreateDisk(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) Start(_ context.Context) (chan error, error) { + return nil, ErrUnsupported +} + +func (l *LimaWslDriver) Stop(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) CanRunGUI() bool { + return false +} + +func (l *LimaWslDriver) RunGUI() error { + return ErrUnsupported +} + +func (l *LimaWslDriver) ChangeDisplayPassword(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) GetDisplayConnection(_ context.Context) (string, error) { + return "", ErrUnsupported +} + +func (l *LimaWslDriver) CreateSnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) ApplySnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) DeleteSnapshot(_ context.Context, _ string) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) ListSnapshots(_ context.Context) (string, error) { + return "", ErrUnsupported +} + +func (l *LimaWslDriver) Register(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) Unregister(_ context.Context) error { + return ErrUnsupported +} + +func (l *LimaWslDriver) ForwardGuestAgent() bool { + return false +} + +func (l *LimaWslDriver) GuestAgentConn(_ context.Context) (net.Conn, error) { + return nil, ErrUnsupported +} + +func (l *LimaWslDriver) VSockPort() int { + return l.vSockPort +} + +func (l *LimaWslDriver) VirtioPort() string { + return l.virtioPort +} diff --git a/pkg/wsl2/wsl_driver_windows.go b/pkg/driver/wsl2/wsl_driver_windows.go similarity index 75% rename from pkg/wsl2/wsl_driver_windows.go rename to pkg/driver/wsl2/wsl_driver_windows.go index 4ad7d472452..cd9df59e34d 100644 --- a/pkg/wsl2/wsl_driver_windows.go +++ b/pkg/driver/wsl2/wsl_driver_windows.go @@ -5,6 +5,7 @@ package wsl2 import ( "context" + "errors" "fmt" "net" "regexp" @@ -47,18 +48,26 @@ var knownYamlProperties = []string{ const Enabled = true type LimaWslDriver struct { - *driver.BaseDriver + Instance *store.Instance + + SSHLocalPort int + vSockPort int + virtioPort string } -func New(driver *driver.BaseDriver) *LimaWslDriver { +var _ driver.Driver = (*LimaWslDriver)(nil) + +func New(inst *store.Instance, sshLocalPort int) *LimaWslDriver { port, err := freeport.VSock() if err != nil { logrus.WithError(err).Error("failed to get free VSock port") } - driver.VSockPort = port - driver.VirtioPort = "" + return &LimaWslDriver{ - BaseDriver: driver, + Instance: inst, + vSockPort: port, + virtioPort: "", + SSHLocalPort: sshLocalPort, } } @@ -123,10 +132,10 @@ func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) { distroName := "lima-" + l.Instance.Name if status == store.StatusUninitialized { - if err := EnsureFs(ctx, l.BaseDriver); err != nil { + if err := EnsureFs(ctx, l.Instance); err != nil { return nil, err } - if err := initVM(ctx, l.BaseDriver.Instance.Dir, distroName); err != nil { + if err := initVM(ctx, l.Instance.Dir, distroName); err != nil { return nil, err } } @@ -139,8 +148,8 @@ func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) { if err := provisionVM( ctx, - l.BaseDriver.Instance.Dir, - l.BaseDriver.Instance.Name, + l.Instance.Dir, + l.Instance.Name, distroName, errCh, ); err != nil { @@ -197,7 +206,56 @@ func (l *LimaWslDriver) GuestAgentConn(ctx context.Context) (net.Conn, error) { } sockAddr := &winio.HvsockAddr{ VMID: VMIDGUID, - ServiceID: winio.VsockServiceID(uint32(l.VSockPort)), + ServiceID: winio.VsockServiceID(uint32(l.vSockPort)), } return winio.Dial(ctx, sockAddr) } + +func (l *LimaWslDriver) Initialize(_ context.Context) error { + return nil +} + +func (l *LimaWslDriver) CreateDisk(_ context.Context) error { + return nil +} + +func (l *LimaWslDriver) Register(_ context.Context) error { + return nil +} + +func (l *LimaWslDriver) ChangeDisplayPassword(_ context.Context, _ string) error { + return nil +} + +func (l *LimaWslDriver) GetDisplayConnection(_ context.Context) (string, error) { + return "", nil +} + +func (l *LimaWslDriver) CreateSnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaWslDriver) ApplySnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaWslDriver) DeleteSnapshot(_ context.Context, _ string) error { + return errors.New("unimplemented") +} + +func (l *LimaWslDriver) ListSnapshots(_ context.Context) (string, error) { + return "", errors.New("unimplemented") +} + +func (l *LimaWslDriver) ForwardGuestAgent() bool { + // If driver is not providing, use host agent + return l.vSockPort == 0 && l.virtioPort == "" +} + +func (l *LimaWslDriver) VSockPort() int { + return l.vSockPort +} + +func (l *LimaWslDriver) VirtioPort() string { + return l.virtioPort +} diff --git a/pkg/driverutil/driverutil.go b/pkg/driverutil/driverutil.go index eb27833e7ad..1a95721105a 100644 --- a/pkg/driverutil/driverutil.go +++ b/pkg/driverutil/driverutil.go @@ -4,9 +4,9 @@ package driverutil import ( + "github.com/lima-vm/lima/pkg/driver/vz" + "github.com/lima-vm/lima/pkg/driver/wsl2" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/vz" - "github.com/lima-vm/lima/pkg/wsl2" ) // Drivers returns the available drivers. diff --git a/pkg/driverutil/instance.go b/pkg/driverutil/instance.go index d7c443ff5d2..0d68f66e1d1 100644 --- a/pkg/driverutil/instance.go +++ b/pkg/driverutil/instance.go @@ -5,19 +5,20 @@ package driverutil import ( "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/driver/qemu" + "github.com/lima-vm/lima/pkg/driver/vz" + "github.com/lima-vm/lima/pkg/driver/wsl2" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/qemu" - "github.com/lima-vm/lima/pkg/vz" - "github.com/lima-vm/lima/pkg/wsl2" + "github.com/lima-vm/lima/pkg/store" ) -func CreateTargetDriverInstance(base *driver.BaseDriver) driver.Driver { - limaDriver := base.Instance.Config.VMType +func CreateTargetDriverInstance(inst *store.Instance, sshLocalPort int) driver.Driver { + limaDriver := inst.Config.VMType if *limaDriver == limayaml.VZ { - return vz.New(base) + return vz.New(inst, sshLocalPort) } if *limaDriver == limayaml.WSL2 { - return wsl2.New(base) + return wsl2.New(inst, sshLocalPort) } - return qemu.New(base) + return qemu.New(inst, sshLocalPort) } diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index c0452c8ba72..22ee61547ce 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -131,13 +131,9 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt } } - baseDriver := driver.BaseDriver{ - Instance: inst, - SSHLocalPort: sshLocalPort, - } - limaDriver := driverutil.CreateTargetDriverInstance(&baseDriver) - vSockPort := baseDriver.VSockPort - virtioPort := baseDriver.VirtioPort + limaDriver := driverutil.CreateTargetDriverInstance(inst, sshLocalPort) + vSockPort := limaDriver.VSockPort() + virtioPort := limaDriver.VirtioPort() if err := cidata.GenerateCloudConfig(inst.Dir, instName, inst.Config); err != nil { return nil, err diff --git a/pkg/imgutil/proxyimgutil/proxyimgutil.go b/pkg/imgutil/proxyimgutil/proxyimgutil.go index 7ff88abcef0..0c56ca82a45 100644 --- a/pkg/imgutil/proxyimgutil/proxyimgutil.go +++ b/pkg/imgutil/proxyimgutil/proxyimgutil.go @@ -10,7 +10,7 @@ import ( "github.com/lima-vm/lima/pkg/imgutil" "github.com/lima-vm/lima/pkg/imgutil/nativeimgutil" - "github.com/lima-vm/lima/pkg/imgutil/qemuimgutil" + "github.com/lima-vm/lima/pkg/qemuimgutil" ) // ImageDiskManager is a proxy implementation of imgutil.ImageDiskManager that uses both QEMU and native image utilities. diff --git a/pkg/instance/create.go b/pkg/instance/create.go index b9088785976..7b69ccc3100 100644 --- a/pkg/instance/create.go +++ b/pkg/instance/create.go @@ -11,7 +11,6 @@ import ( "path/filepath" "github.com/lima-vm/lima/pkg/cidata" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/driverutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/osutil" @@ -76,9 +75,7 @@ func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenY return nil, err } - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) if err := limaDriver.Register(ctx); err != nil { return nil, err diff --git a/pkg/instance/delete.go b/pkg/instance/delete.go index c4078d48a11..ca9a8ae3c01 100644 --- a/pkg/instance/delete.go +++ b/pkg/instance/delete.go @@ -9,7 +9,6 @@ import ( "fmt" "os" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/driverutil" "github.com/lima-vm/lima/pkg/store" ) @@ -37,9 +36,7 @@ func Delete(ctx context.Context, inst *store.Instance, force bool) error { } func unregister(ctx context.Context, inst *store.Instance) error { - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) return limaDriver.Unregister(ctx) } diff --git a/pkg/instance/start.go b/pkg/instance/start.go index acd3741e0bd..d0c344fc8d7 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -90,9 +90,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) { return nil, err } } - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) if err := limaDriver.Validate(); err != nil { return nil, err @@ -107,6 +105,43 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) { _, err := os.Stat(baseDisk) created := err == nil + kernel := filepath.Join(inst.Dir, filenames.Kernel) + kernelCmdline := filepath.Join(inst.Dir, filenames.KernelCmdline) + initrd := filepath.Join(inst.Dir, filenames.Initrd) + if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) { + var ensuredBaseDisk bool + errs := make([]error, len(inst.Config.Images)) + for i, f := range inst.Config.Images { + if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *inst.Config.Arch); err != nil { + errs[i] = err + continue + } + if f.Kernel != nil { + if _, err := fileutils.DownloadFile(ctx, kernel, f.Kernel.File, false, "the kernel", *inst.Config.Arch); err != nil { + errs[i] = err + continue + } + if f.Kernel.Cmdline != "" { + if err := os.WriteFile(kernelCmdline, []byte(f.Kernel.Cmdline), 0o644); err != nil { + errs[i] = err + continue + } + } + } + if f.Initrd != nil { + if _, err := fileutils.DownloadFile(ctx, initrd, *f.Initrd, false, "the initrd", *inst.Config.Arch); err != nil { + errs[i] = err + continue + } + } + ensuredBaseDisk = true + break + } + if !ensuredBaseDisk { + return nil, fileutils.Errors(errs) + } + } + if err := limaDriver.CreateDisk(ctx); err != nil { return nil, err } diff --git a/pkg/networks/usernet/client.go b/pkg/networks/usernet/client.go index 2296f103ed1..8bc478cb0f2 100644 --- a/pkg/networks/usernet/client.go +++ b/pkg/networks/usernet/client.go @@ -17,10 +17,10 @@ import ( gvproxyclient "github.com/containers/gvisor-tap-vsock/pkg/client" "github.com/containers/gvisor-tap-vsock/pkg/types" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/httpclientutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks/usernet/dnshosts" + "github.com/lima-vm/lima/pkg/store" ) type Client struct { @@ -32,18 +32,18 @@ type Client struct { subnet net.IP } -func (c *Client) ConfigureDriver(ctx context.Context, driver *driver.BaseDriver) error { - macAddress := limayaml.MACAddress(driver.Instance.Dir) +func (c *Client) ConfigureDriver(ctx context.Context, inst *store.Instance, sshLocalPort int) error { + macAddress := limayaml.MACAddress(inst.Dir) ipAddress, err := c.ResolveIPAddress(ctx, macAddress) if err != nil { return err } - err = c.ResolveAndForwardSSH(ipAddress, driver.SSHLocalPort) + err = c.ResolveAndForwardSSH(ipAddress, sshLocalPort) if err != nil { return err } - hosts := driver.Instance.Config.HostResolver.Hosts - hosts[fmt.Sprintf("%s.internal", driver.Instance.Hostname)] = ipAddress + hosts := inst.Config.HostResolver.Hosts + hosts[fmt.Sprintf("%s.internal", inst.Hostname)] = ipAddress err = c.AddDNSHosts(hosts) return err } diff --git a/pkg/imgutil/qemuimgutil/qemuimgutil.go b/pkg/qemuimgutil/qemuimgutil.go similarity index 100% rename from pkg/imgutil/qemuimgutil/qemuimgutil.go rename to pkg/qemuimgutil/qemuimgutil.go diff --git a/pkg/imgutil/qemuimgutil/qemuimgutil_test.go b/pkg/qemuimgutil/qemuimgutil_test.go similarity index 100% rename from pkg/imgutil/qemuimgutil/qemuimgutil_test.go rename to pkg/qemuimgutil/qemuimgutil_test.go diff --git a/pkg/snapshot/snapshot.go b/pkg/snapshot/snapshot.go index 520a41d92a4..9c48fc0c50a 100644 --- a/pkg/snapshot/snapshot.go +++ b/pkg/snapshot/snapshot.go @@ -6,35 +6,26 @@ package snapshot import ( "context" - "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/driverutil" "github.com/lima-vm/lima/pkg/store" ) func Del(ctx context.Context, inst *store.Instance, tag string) error { - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) return limaDriver.DeleteSnapshot(ctx, tag) } func Save(ctx context.Context, inst *store.Instance, tag string) error { - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) return limaDriver.CreateSnapshot(ctx, tag) } func Load(ctx context.Context, inst *store.Instance, tag string) error { - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) return limaDriver.ApplySnapshot(ctx, tag) } func List(ctx context.Context, inst *store.Instance) (string, error) { - limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{ - Instance: inst, - }) + limaDriver := driverutil.CreateTargetDriverInstance(inst, 0) return limaDriver.ListSnapshots(ctx) } diff --git a/pkg/vz/disk.go b/pkg/vz/disk.go deleted file mode 100644 index 06194faf5d2..00000000000 --- a/pkg/vz/disk.go +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package vz - -import ( - "context" - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/docker/go-units" - - "github.com/lima-vm/lima/pkg/driver" - "github.com/lima-vm/lima/pkg/fileutils" - "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" - "github.com/lima-vm/lima/pkg/iso9660util" - "github.com/lima-vm/lima/pkg/store/filenames" -) - -func EnsureDisk(ctx context.Context, driver *driver.BaseDriver) error { - diffDisk := filepath.Join(driver.Instance.Dir, filenames.DiffDisk) - if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) { - // disk is already ensured - return err - } - - diskUtil := proxyimgutil.NewDiskUtil() - - baseDisk := filepath.Join(driver.Instance.Dir, filenames.BaseDisk) - kernel := filepath.Join(driver.Instance.Dir, filenames.Kernel) - kernelCmdline := filepath.Join(driver.Instance.Dir, filenames.KernelCmdline) - initrd := filepath.Join(driver.Instance.Dir, filenames.Initrd) - if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) { - var ensuredBaseDisk bool - errs := make([]error, len(driver.Instance.Config.Images)) - for i, f := range driver.Instance.Config.Images { - if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *driver.Instance.Config.Arch); err != nil { - errs[i] = err - continue - } - if f.Kernel != nil { - // ensure decompress kernel because vz expects it to be decompressed - if _, err := fileutils.DownloadFile(ctx, kernel, f.Kernel.File, true, "the kernel", *driver.Instance.Config.Arch); err != nil { - errs[i] = err - continue - } - if f.Kernel.Cmdline != "" { - if err := os.WriteFile(kernelCmdline, []byte(f.Kernel.Cmdline), 0o644); err != nil { - errs[i] = err - continue - } - } - } - if f.Initrd != nil { - if _, err := fileutils.DownloadFile(ctx, initrd, *f.Initrd, false, "the initrd", *driver.Instance.Config.Arch); err != nil { - errs[i] = err - continue - } - } - ensuredBaseDisk = true - break - } - if !ensuredBaseDisk { - return fileutils.Errors(errs) - } - } - diskSize, _ := units.RAMInBytes(*driver.Instance.Config.Disk) - if diskSize == 0 { - return nil - } - isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk) - if err != nil { - return err - } - if isBaseDiskISO { - // Create an empty data volume (sparse) - diffDiskF, err := os.Create(diffDisk) - if err != nil { - return err - } - - err = diskUtil.MakeSparse(diffDiskF, 0) - if err != nil { - diffDiskF.Close() - return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err) - } - return diffDiskF.Close() - } - if err = diskUtil.ConvertToRaw(baseDisk, diffDisk, &diskSize, false); err != nil { - return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err) - } - return err -} diff --git a/pkg/vz/vz_driver_others.go b/pkg/vz/vz_driver_others.go deleted file mode 100644 index 66265628a49..00000000000 --- a/pkg/vz/vz_driver_others.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build !darwin || no_vz - -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package vz - -import ( - "context" - "errors" - - "github.com/lima-vm/lima/pkg/driver" -) - -var ErrUnsupported = errors.New("vm driver 'vz' needs macOS 13 or later (Hint: try recompiling Lima if you are seeing this error on macOS 13)") - -const Enabled = false - -type LimaVzDriver struct { - *driver.BaseDriver -} - -func New(driver *driver.BaseDriver) *LimaVzDriver { - return &LimaVzDriver{ - BaseDriver: driver, - } -} - -func (l *LimaVzDriver) Validate() error { - return ErrUnsupported -} - -func (l *LimaVzDriver) CreateDisk(_ context.Context) error { - return ErrUnsupported -} - -func (l *LimaVzDriver) Start(_ context.Context) (chan error, error) { - return nil, ErrUnsupported -} - -func (l *LimaVzDriver) Stop(_ context.Context) error { - return ErrUnsupported -} diff --git a/pkg/wsl2/wsl_driver_others.go b/pkg/wsl2/wsl_driver_others.go deleted file mode 100644 index 4ad86d02768..00000000000 --- a/pkg/wsl2/wsl_driver_others.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build !windows || no_wsl - -// SPDX-FileCopyrightText: Copyright The Lima Authors -// SPDX-License-Identifier: Apache-2.0 - -package wsl2 - -import ( - "context" - "errors" - - "github.com/lima-vm/lima/pkg/driver" -) - -var ErrUnsupported = errors.New("vm driver 'wsl2' requires Windows 10 build 19041 or later (Hint: try recompiling Lima if you are seeing this error on Windows 10+)") - -const Enabled = false - -type LimaWslDriver struct { - *driver.BaseDriver -} - -func New(driver *driver.BaseDriver) *LimaWslDriver { - return &LimaWslDriver{ - BaseDriver: driver, - } -} - -func (l *LimaWslDriver) Validate() error { - return ErrUnsupported -} - -func (l *LimaWslDriver) CreateDisk(_ context.Context) error { - return ErrUnsupported -} - -func (l *LimaWslDriver) Start(_ context.Context) (chan error, error) { - return nil, ErrUnsupported -} - -func (l *LimaWslDriver) Stop(_ context.Context) error { - return ErrUnsupported -} From 5a9c69c87151528b9bb86eeb7cef690cc39c742b Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Mon, 7 Jul 2025 13:09:29 -0700 Subject: [PATCH 78/91] Bump alpine-lima to v0.2.44 It now bundles the iproute2 package which adds `ip -j a` support. Alpine itself is bumped from 3.20.3 to 3.21.3. Signed-off-by: Jan Dubois --- templates/_images/alpine-iso.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/_images/alpine-iso.yaml b/templates/_images/alpine-iso.yaml index dd79496a72b..875fac71dd9 100644 --- a/templates/_images/alpine-iso.yaml +++ b/templates/_images/alpine-iso.yaml @@ -1,9 +1,9 @@ -# Using the Alpine 3.20 aarch64 image with vmType=vz requires macOS Ventura 13.3 or later. +# Using the Alpine 3.21 aarch64 image with vmType=vz requires macOS Ventura 13.3 or later. images: -- location: https://github.com/lima-vm/alpine-lima/releases/download/v0.2.41/alpine-lima-std-3.20.3-x86_64.iso +- location: https://github.com/lima-vm/alpine-lima/releases/download/v0.2.44/alpine-lima-std-3.21.3-x86_64.iso arch: x86_64 - digest: sha512:949a353c1676bb406561d22c1b7f9db72fb0cc899c6c50166df3b38e392280a7e7b83f58643a309816d51a48317507c46c3e7e24e52fbc9f20fe817039306db1 + digest: sha512:60d8c857ee2654ab26d3b7a68f3790e044cdef13e297e18167c2dcc885648a7b86100a4348aee03362edaa212c90b01c3c473c9b5a10500ba95ae6e9d0add2a5 -- location: https://github.com/lima-vm/alpine-lima/releases/download/v0.2.41/alpine-lima-std-3.20.3-aarch64.iso +- location: https://github.com/lima-vm/alpine-lima/releases/download/v0.2.44/alpine-lima-std-3.21.3-aarch64.iso arch: aarch64 - digest: sha512:91ea119fea2bb638519792de2047303b26eaebcdace8df57b76373dc7b1cddcad77aaa9fed2d438fb02351b261783af3264d6bb2716519f8ba211a4b25d6f114 + digest: sha512:ac421aa43b791f41edbebe4d1ceaee34bf68876af1c75c1992bb497c7411b14a95f51294f09e44beb1cb029ef36e5a6262b01786a08737de07dca92599eb20e2 From 0f36e75b892ce0ac6061690214e62a9f1c601a8b Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 3 Jul 2025 16:09:43 +0900 Subject: [PATCH 79/91] Implement `limactl network (list|create|delete)` Fix issue 3672 Signed-off-by: Akihiro Suda --- cmd/limactl/completion.go | 25 ++++ cmd/limactl/disk.go | 10 +- cmd/limactl/main.go | 1 + cmd/limactl/network.go | 271 ++++++++++++++++++++++++++++++++++++++ pkg/networks/networks.go | 29 ++-- 5 files changed, 320 insertions(+), 16 deletions(-) create mode 100644 cmd/limactl/network.go diff --git a/cmd/limactl/completion.go b/cmd/limactl/completion.go index f69005b9d31..f68f463b5cc 100644 --- a/cmd/limactl/completion.go +++ b/cmd/limactl/completion.go @@ -4,10 +4,14 @@ package main import ( + "maps" + "net" + "slices" "strings" "github.com/spf13/cobra" + "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/templatestore" ) @@ -51,3 +55,24 @@ func bashCompleteDiskNames(_ *cobra.Command) ([]string, cobra.ShellCompDirective } return disks, cobra.ShellCompDirectiveNoFileComp } + +func bashCompleteNetworkNames(_ *cobra.Command) ([]string, cobra.ShellCompDirective) { + config, err := networks.LoadConfig() + if err != nil { + return nil, cobra.ShellCompDirectiveDefault + } + networks := slices.Sorted(maps.Keys(config.Networks)) + return networks, cobra.ShellCompDirectiveNoFileComp +} + +func bashFlagCompleteNetworkInterfaceNames(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + intf, err := net.Interfaces() + if err != nil { + return nil, cobra.ShellCompDirectiveDefault + } + var intfNames []string + for _, f := range intf { + intfNames = append(intfNames, f.Name) + } + return intfNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go index 7f19b069c5d..f6b035b11d7 100644 --- a/cmd/limactl/disk.go +++ b/cmd/limactl/disk.go @@ -141,11 +141,11 @@ $ limactl disk list return diskListCommand } -func diskMatches(diskName string, disks []string) []string { +func nameMatches(nameName string, names []string) []string { matches := []string{} - for _, disk := range disks { - if disk == diskName { - matches = append(matches, disk) + for _, name := range names { + if name == nameName { + matches = append(matches, name) } } return matches @@ -165,7 +165,7 @@ func diskListAction(cmd *cobra.Command, args []string) error { disks := []string{} if len(args) > 0 { for _, arg := range args { - matches := diskMatches(arg, allDisks) + matches := nameMatches(arg, allDisks) if len(matches) > 0 { disks = append(disks, matches...) } else { diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go index a006f0bb33f..acfa41074e2 100644 --- a/cmd/limactl/main.go +++ b/cmd/limactl/main.go @@ -189,6 +189,7 @@ func newApp() *cobra.Command { newRestartCommand(), newSudoersCommand(), newStartAtLoginCommand(), + newNetworkCommand(), ) return rootCmd diff --git a/cmd/limactl/network.go b/cmd/limactl/network.go new file mode 100644 index 00000000000..7fd32ff293a --- /dev/null +++ b/cmd/limactl/network.go @@ -0,0 +1,271 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "encoding/json" + "errors" + "fmt" + "maps" + "net" + "os" + "slices" + "strings" + "text/tabwriter" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/lima-vm/lima/pkg/networks" + "github.com/lima-vm/lima/pkg/yqutil" +) + +const networkCreateExample = ` Create a network: + $ limactl network create foo --gateway 192.168.42.1/24 + + Connect VM instances to the newly created network: + $ limactl create --network lima:foo --name vm1 + $ limactl create --network lima:foo --name vm2 +` + +func newNetworkCommand() *cobra.Command { + networkCommand := &cobra.Command{ + Use: "network", + Short: "Lima network management", + Example: networkCreateExample, + GroupID: advancedCommand, + } + networkCommand.AddCommand( + newNetworkListCommand(), + newNetworkCreateCommand(), + newNetworkDeleteCommand(), + ) + return networkCommand +} + +func newNetworkListCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List networks", + Aliases: []string{"ls"}, + Args: WrapArgsError(cobra.ArbitraryArgs), + RunE: networkListAction, + ValidArgsFunction: networkBashComplete, + } + flags := cmd.Flags() + flags.Bool("json", false, "JSONify output") + return cmd +} + +func networkListAction(cmd *cobra.Command, args []string) error { + flags := cmd.Flags() + jsonFormat, err := flags.GetBool("json") + if err != nil { + return err + } + + config, err := networks.LoadConfig() + if err != nil { + return err + } + + allNetworks := slices.Sorted(maps.Keys(config.Networks)) + + networks := []string{} + if len(args) > 0 { + for _, arg := range args { + matches := nameMatches(arg, allNetworks) + if len(matches) > 0 { + networks = append(networks, matches...) + } else { + logrus.Warnf("No network matching %v found.", arg) + } + } + } else { + networks = allNetworks + } + + if jsonFormat { + w := cmd.OutOrStdout() + for _, name := range networks { + nw, ok := config.Networks[name] + if !ok { + logrus.Errorf("network %q does not exist", nw) + continue + } + j, err := json.Marshal(nw) + if err != nil { + return err + } + fmt.Fprintln(w, string(j)) + } + return nil + } + + w := tabwriter.NewWriter(cmd.OutOrStdout(), 4, 8, 4, ' ', 0) + fmt.Fprintln(w, "NAME\tMODE\tGATEWAY\tINTERFACE") + for _, name := range networks { + nw, ok := config.Networks[name] + if !ok { + logrus.Errorf("network %q does not exist", nw) + continue + } + gwStr := "-" + if nw.Gateway != nil { + gw := net.IPNet{ + IP: nw.Gateway, + Mask: net.IPMask(nw.NetMask), + } + gwStr = gw.String() + } + intfStr := "-" + if nw.Interface != "" { + intfStr = nw.Interface + } + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, nw.Mode, gwStr, intfStr) + } + return w.Flush() +} + +func newNetworkCreateCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "create NETWORK", + Short: "Create a Lima network", + Example: networkCreateExample, + Args: WrapArgsError(cobra.ExactArgs(1)), + RunE: networkCreateAction, + } + flags := cmd.Flags() + flags.String("mode", networks.ModeUserV2, "mode") + _ = cmd.RegisterFlagCompletionFunc("mode", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return networks.Modes, cobra.ShellCompDirectiveNoFileComp + }) + flags.String("gateway", "", "gateway, e.g., \"192.168.42.1/24\"") + flags.String("interface", "", "interface for bridged mode") + _ = cmd.RegisterFlagCompletionFunc("interface", bashFlagCompleteNetworkInterfaceNames) + return cmd +} + +func networkCreateAction(cmd *cobra.Command, args []string) error { + name := args[0] + // LoadConfig ensures existence of networks.yaml + config, err := networks.LoadConfig() + if err != nil { + return err + } + if _, ok := config.Networks[name]; ok { + return fmt.Errorf("network %q already exists", name) + } + + flags := cmd.Flags() + mode, err := flags.GetString("mode") + if err != nil { + return err + } + + gateway, err := flags.GetString("gateway") + if err != nil { + return err + } + + intf, err := flags.GetString("interface") + if err != nil { + return err + } + + switch mode { + case networks.ModeBridged: + if gateway != "" { + return fmt.Errorf("network mode %q does not support specifying gateway", mode) + } + if intf == "" { + return fmt.Errorf("network mode %q requires specifying interface", mode) + } + default: + if gateway == "" { + return fmt.Errorf("network mode %q requires specifying gateway", mode) + } + if intf != "" { + return fmt.Errorf("network mode %q does not support specifying interface", mode) + } + } + + if !strings.Contains(gateway, "/") { + gateway += "/24" + } + gwIP, gwMask, err := net.ParseCIDR(gateway) + if err != nil { + return fmt.Errorf("failed to parse CIDR %q: %w", gateway, err) + } + if gwIP.IsUnspecified() || gwIP.IsLoopback() { + return fmt.Errorf("invalid IP address: %v", gwIP) + } + gwMaskStr := "255.255.255.0" + if gwMask != nil { + gwMaskStr = net.IP(gwMask.Mask).String() + } + // TODO: check IP range collision + + yq := fmt.Sprintf(`.networks.%q = {"mode":%q,"gateway":%q,"netmask":%q,"interface":%q}`, name, mode, gwIP.String(), gwMaskStr, intf) + + return networkApplyYQ(yq) +} + +func networkApplyYQ(yq string) error { + filePath, err := networks.ConfigFile() + if err != nil { + return err + } + yContent, err := os.ReadFile(filePath) + if err != nil { + return err + } + yBytes, err := yqutil.EvaluateExpression(yq, yContent) + if err != nil { + return err + } + if err := os.WriteFile(filePath, yBytes, 0o644); err != nil { + return err + } + return nil +} + +func newNetworkDeleteCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete NETWORK [NETWORK, ...]", + Short: "Delete one or more Lima networks", + Aliases: []string{"remove", "rm"}, + Args: WrapArgsError(cobra.MinimumNArgs(1)), + RunE: networkDeleteAction, + ValidArgsFunction: networkBashComplete, + } + flags := cmd.Flags() + flags.BoolP("force", "f", false, "Force delete (currently always required)") + return cmd +} + +func networkDeleteAction(cmd *cobra.Command, args []string) error { + flags := cmd.Flags() + force, err := flags.GetBool("force") + if err != nil { + return err + } + if !force { + return errors.New("`limactl network delete` currently always requires `--force`") + // Because the command currently does not check whether the network being removed is in use + } + + var yq string + for i, name := range args { + yq += fmt.Sprintf("del(.networks.%q)", name) + if i < len(args)-1 { + yq += " | " + } + } + return networkApplyYQ(yq) +} + +func networkBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return bashCompleteNetworkNames(cmd) +} diff --git a/pkg/networks/networks.go b/pkg/networks/networks.go index 12a1555d718..717627fb0da 100644 --- a/pkg/networks/networks.go +++ b/pkg/networks/networks.go @@ -6,15 +6,15 @@ package networks import "net" type Config struct { - Paths Paths `yaml:"paths"` - Group string `yaml:"group,omitempty"` // default: "everyone" - Networks map[string]Network `yaml:"networks"` + Paths Paths `yaml:"paths" json:"paths"` + Group string `yaml:"group,omitempty" json:"group,omitempty"` // default: "everyone" + Networks map[string]Network `yaml:"networks" json:"networks"` } type Paths struct { - SocketVMNet string `yaml:"socketVMNet"` - VarRun string `yaml:"varRun"` - Sudoers string `yaml:"sudoers,omitempty"` + SocketVMNet string `yaml:"socketVMNet" json:"socketVMNet"` + VarRun string `yaml:"varRun" json:"varRun"` + Sudoers string `yaml:"sudoers,omitempty" json:"sudoers,omitempty"` } const ( @@ -24,10 +24,17 @@ const ( ModeBridged = "bridged" ) +var Modes = []string{ + ModeUserV2, + ModeHost, + ModeShared, + ModeBridged, +} + type Network struct { - Mode string `yaml:"mode"` // "host", "shared", or "bridged" - Interface string `yaml:"interface,omitempty"` // only used by "bridged" networks - Gateway net.IP `yaml:"gateway,omitempty"` // only used by "host" and "shared" networks - DHCPEnd net.IP `yaml:"dhcpEnd,omitempty"` // default: same as Gateway, last byte is 254 - NetMask net.IP `yaml:"netmask,omitempty"` // default: 255.255.255.0 + Mode string `yaml:"mode" json:"mode"` // "user-v2", "host", "shared", or "bridged" + Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` // only used by "bridged" networks + Gateway net.IP `yaml:"gateway,omitempty" json:"gateway,omitempty"` // only used by "user-v2", "host" and "shared" networks + DHCPEnd net.IP `yaml:"dhcpEnd,omitempty" json:"dhcpEnd,omitempty"` // default: same as Gateway, last byte is 254 + NetMask net.IP `yaml:"netmask,omitempty" json:"netmask,omitempty"` // default: 255.255.255.0 } From 2a4e2ea5205c13a49cae2b96a5767210ef405e5b Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 8 Jul 2025 15:13:56 +0900 Subject: [PATCH 80/91] nerdctl: update from v2.1.2 to v2.1.3 https://github.com/containerd/nerdctl/releases/tag/v2.1.3 Signed-off-by: Akihiro Suda --- pkg/limayaml/containerd.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/limayaml/containerd.yaml b/pkg/limayaml/containerd.yaml index 43163b535a0..b78207d15ac 100644 --- a/pkg/limayaml/containerd.yaml +++ b/pkg/limayaml/containerd.yaml @@ -1,9 +1,9 @@ archives: -- location: https://github.com/containerd/nerdctl/releases/download/v2.1.2/nerdctl-full-2.1.2-linux-amd64.tar.gz +- location: https://github.com/containerd/nerdctl/releases/download/v2.1.3/nerdctl-full-2.1.3-linux-amd64.tar.gz arch: x86_64 - digest: sha256:b3ab8564c8fa6feb89d09bee881211b700b047373c767bec38256d0d68f93074 -- location: https://github.com/containerd/nerdctl/releases/download/v2.1.2/nerdctl-full-2.1.2-linux-arm64.tar.gz + digest: sha256:e4750ba025c1a9b3d365285f800e683287ee782f0a878bc8c395f28d7bc194ba +- location: https://github.com/containerd/nerdctl/releases/download/v2.1.3/nerdctl-full-2.1.3-linux-arm64.tar.gz arch: aarch64 - digest: sha256:1b52f32b7d5bbf63005bceb6a3cacd237d2fa8f1d05bb590e8ce58731779b9ee + digest: sha256:544fa1e518155fcc01a117ea49819d12d96b4dacfb2b62922f9f7956dc9f6dc8 # No arm-v7 # No riscv64 From 2f7c4013baa3fb6bb72b0c5de25c704931f7fbaf Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 1 Jul 2025 14:12:43 +0900 Subject: [PATCH 81/91] Implement `limactl clone` `limactl clone OLDINST NEWINST` clones an instance. Not to be confused with `limactl copy SRC DST` (copy files). Fix issue 3658 Signed-off-by: Akihiro Suda --- cmd/limactl/clone.go | 117 +++++++++++++++++++++++++++++ cmd/limactl/copy.go | 2 + cmd/limactl/edit.go | 2 +- cmd/limactl/editflags/editflags.go | 8 +- cmd/limactl/main.go | 1 + hack/test-templates.sh | 12 +++ pkg/driver/vz/vm_darwin.go | 7 +- pkg/instance/clone.go | 90 ++++++++++++++++++++++ pkg/instance/stop.go | 5 +- pkg/store/filenames/filenames.go | 14 ++++ 10 files changed, 247 insertions(+), 11 deletions(-) create mode 100644 cmd/limactl/clone.go create mode 100644 pkg/instance/clone.go diff --git a/cmd/limactl/clone.go b/cmd/limactl/clone.go new file mode 100644 index 00000000000..694c3488a6a --- /dev/null +++ b/cmd/limactl/clone.go @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + + "github.com/lima-vm/lima/cmd/limactl/editflags" + "github.com/lima-vm/lima/pkg/instance" + "github.com/lima-vm/lima/pkg/limayaml" + networks "github.com/lima-vm/lima/pkg/networks/reconcile" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" + "github.com/lima-vm/lima/pkg/yqutil" +) + +func newCloneCommand() *cobra.Command { + cloneCommand := &cobra.Command{ + Use: "clone OLDINST NEWINST", + Short: "Clone an instance of Lima", + Long: `Clone an instance of Lima. + +Not to be confused with 'limactl copy' ('limactl cp'). +`, + Args: WrapArgsError(cobra.ExactArgs(2)), + RunE: cloneAction, + ValidArgsFunction: cloneBashComplete, + GroupID: advancedCommand, + } + editflags.RegisterEdit(cloneCommand, "[limactl edit] ") + return cloneCommand +} + +func cloneAction(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + flags := cmd.Flags() + tty, err := flags.GetBool("tty") + if err != nil { + return err + } + + oldInstName, newInstName := args[0], args[1] + oldInst, err := store.Inspect(oldInstName) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("instance %q not found", oldInstName) + } + return err + } + + newInst, err := instance.Clone(ctx, oldInst, newInstName) + if err != nil { + return err + } + + yqExprs, err := editflags.YQExpressions(flags, false) + if err != nil { + return err + } + if len(yqExprs) > 0 { + // TODO: reduce duplicated codes across cloneAction and editAction + yq := yqutil.Join(yqExprs) + filePath := filepath.Join(newInst.Dir, filenames.LimaYAML) + yContent, err := os.ReadFile(filePath) + if err != nil { + return err + } + yBytes, err := yqutil.EvaluateExpression(yq, yContent) + if err != nil { + return err + } + y, err := limayaml.LoadWithWarnings(yBytes, filePath) + if err != nil { + return err + } + if err := limayaml.Validate(y, true); err != nil { + return saveRejectedYAML(yBytes, err) + } + if err := limayaml.ValidateAgainstLatestConfig(yBytes, yContent); err != nil { + return saveRejectedYAML(yBytes, err) + } + if err := os.WriteFile(filePath, yBytes, 0o644); err != nil { + return err + } + newInst, err = store.Inspect(newInst.Name) + if err != nil { + return err + } + } + + if !tty { + // use "start" to start it + return nil + } + startNow, err := askWhetherToStart() + if err != nil { + return err + } + if !startNow { + return nil + } + err = networks.Reconcile(ctx, newInst.Name) + if err != nil { + return err + } + return instance.Start(ctx, newInst, "", false) +} + +func cloneBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return bashCompleteInstanceNames(cmd) +} diff --git a/cmd/limactl/copy.go b/cmd/limactl/copy.go index 1f422d210a6..832c3fdb0a6 100644 --- a/cmd/limactl/copy.go +++ b/cmd/limactl/copy.go @@ -26,6 +26,8 @@ const copyHelp = `Copy files between host and guest Prefix guest filenames with the instance name and a colon. Example: limactl copy default:/etc/os-release . + +Not to be confused with 'limactl clone'. ` func newCopyCommand() *cobra.Command { diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index 6474d5e8bef..0265e0ad5e3 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -33,7 +33,7 @@ func newEditCommand() *cobra.Command { ValidArgsFunction: editBashComplete, GroupID: basicCommand, } - editflags.RegisterEdit(editCommand) + editflags.RegisterEdit(editCommand, "") return editCommand } diff --git a/cmd/limactl/editflags/editflags.go b/cmd/limactl/editflags/editflags.go index ae9fa593e4f..9f4e7c601af 100644 --- a/cmd/limactl/editflags/editflags.go +++ b/cmd/limactl/editflags/editflags.go @@ -18,11 +18,7 @@ import ( ) // RegisterEdit registers flags related to in-place YAML modification, for `limactl edit`. -func RegisterEdit(cmd *cobra.Command) { - registerEdit(cmd, "") -} - -func registerEdit(cmd *cobra.Command, commentPrefix string) { +func RegisterEdit(cmd *cobra.Command, commentPrefix string) { flags := cmd.Flags() flags.Int("cpus", 0, commentPrefix+"Number of CPUs") // Similar to colima's --cpu, but the flag name is slightly different (cpu vs cpus) @@ -77,7 +73,7 @@ func registerEdit(cmd *cobra.Command, commentPrefix string) { // RegisterCreate registers flags related to in-place YAML modification, for `limactl create`. func RegisterCreate(cmd *cobra.Command, commentPrefix string) { - registerEdit(cmd, commentPrefix) + RegisterEdit(cmd, commentPrefix) flags := cmd.Flags() flags.String("arch", "", commentPrefix+"Machine architecture (x86_64, aarch64, riscv64, armv7l, s390x, ppc64le)") // colima-compatible diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go index acfa41074e2..5b256900be6 100644 --- a/cmd/limactl/main.go +++ b/cmd/limactl/main.go @@ -190,6 +190,7 @@ func newApp() *cobra.Command { newSudoersCommand(), newStartAtLoginCommand(), newNetworkCommand(), + newCloneCommand(), ) return rootCmd diff --git a/hack/test-templates.sh b/hack/test-templates.sh index 2935907c99e..0dd6f75c335 100755 --- a/hack/test-templates.sh +++ b/hack/test-templates.sh @@ -49,6 +49,7 @@ declare -A CHECKS=( # snapshot tests are too flaky (especially with archlinux) ["snapshot-online"]="" ["snapshot-offline"]="" + ["clone"]="" ["port-forwards"]="1" ["vmnet"]="" ["disk"]="" @@ -85,6 +86,7 @@ case "$NAME" in CHECKS["disk"]=1 CHECKS["snapshot-online"]="1" CHECKS["snapshot-offline"]="1" + CHECKS["clone"]="1" CHECKS["mount-path-with-spaces"]="1" CHECKS["provision-data"]="1" CHECKS["param-env-variables"]="1" @@ -527,6 +529,16 @@ if [[ -n ${CHECKS["snapshot-offline"]} ]]; then limactl snapshot delete "$NAME" --tag snap2 limactl start "$NAME" fi +if [[ -n ${CHECKS["clone"]} ]]; then + INFO "Testing cloning" + limactl stop "$NAME" + sleep 3 + # [hostagent] could not attach disk \"data\", in use by instance \"test-misc-clone\" + limactl clone --set '.additionalDisks = null' "$NAME" "${NAME}-clone" + limactl start "${NAME}-clone" + [ "$(limactl shell "${NAME}-clone" hostname)" = "lima-${NAME}-clone" ] + limactl start "$NAME" +fi if [[ $NAME == "fedora" && "$(limactl ls --json "$NAME" | jq -r .vmType)" == "vz" ]]; then "${scriptdir}"/test-selinux.sh "$NAME" diff --git a/pkg/driver/vz/vm_darwin.go b/pkg/driver/vz/vm_darwin.go index 147af63fddb..2f1351dfb62 100644 --- a/pkg/driver/vz/vm_darwin.go +++ b/pkg/driver/vz/vm_darwin.go @@ -9,6 +9,7 @@ import ( "context" "errors" "fmt" + "io/fs" "net" "os" "path/filepath" @@ -691,7 +692,11 @@ func attachOtherDevices(_ *store.Instance, vmConfig *vz.VirtualMachineConfigurat func getMachineIdentifier(inst *store.Instance) (*vz.GenericMachineIdentifier, error) { identifier := filepath.Join(inst.Dir, filenames.VzIdentifier) - if _, err := os.Stat(identifier); os.IsNotExist(err) { + // Empty VzIdentifier can be created on cloning an instance. + if st, err := os.Stat(identifier); err != nil || (st != nil && st.Size() == 0) { + if err != nil && !errors.Is(err, fs.ErrNotExist) { + return nil, err + } machineIdentifier, err := vz.NewGenericMachineIdentifier() if err != nil { return nil, err diff --git a/pkg/instance/clone.go b/pkg/instance/clone.go new file mode 100644 index 00000000000..b41a7ed614c --- /dev/null +++ b/pkg/instance/clone.go @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package instance + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + + continuityfs "github.com/containerd/continuity/fs" + + "github.com/lima-vm/lima/pkg/osutil" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" +) + +func Clone(_ context.Context, oldInst *store.Instance, newInstName string) (*store.Instance, error) { + if newInstName == "" { + return nil, errors.New("got empty instName") + } + if oldInst.Name == newInstName { + return nil, fmt.Errorf("new instance name %q must be different from %q", newInstName, oldInst.Name) + } + if oldInst.Status == store.StatusRunning { + return nil, errors.New("cannot clone a running instance") + } + + newInstDir, err := store.InstanceDir(newInstName) + if err != nil { + return nil, err + } + + if _, err = os.Stat(newInstDir); !errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("instance %q already exists", newInstName) + } + + // the full path of the socket name must be less than UNIX_PATH_MAX chars. + maxSockName := filepath.Join(newInstDir, filenames.LongestSock) + if len(maxSockName) >= osutil.UnixPathMax { + return nil, fmt.Errorf("instance name %q too long: %q must be less than UNIX_PATH_MAX=%d characters, but is %d", + newInstName, maxSockName, osutil.UnixPathMax, len(maxSockName)) + } + + if err = os.Mkdir(newInstDir, 0o700); err != nil { + return nil, err + } + + walkDirFn := func(path string, d fs.DirEntry, err error) error { + base := filepath.Base(path) + if slices.Contains(filenames.SkipOnClone, base) { + return nil + } + for _, ext := range filenames.TmpFileSuffixes { + if strings.HasSuffix(path, ext) { + return nil + } + } + if err != nil { + return err + } + pathRel, err := filepath.Rel(oldInst.Dir, path) + if err != nil { + return err + } + dst := filepath.Join(newInstDir, pathRel) + if d.IsDir() { + return os.MkdirAll(dst, d.Type().Perm()) + } + // NullifyOnClone contains VzIdentifier. + // VzIdentifier file must not be just removed here, as pkg/limayaml depends on + // the existence of VzIdentifier for resolving the VM type. + if slices.Contains(filenames.NullifyOnClone, base) { + return os.WriteFile(dst, nil, 0o666) + } + // CopyFile attempts copy-on-write when supported by the filesystem + return continuityfs.CopyFile(dst, path) + } + + if err = filepath.WalkDir(oldInst.Dir, walkDirFn); err != nil { + return nil, err + } + + return store.Inspect(newInstName) +} diff --git a/pkg/instance/stop.go b/pkg/instance/stop.go index 60b8d9b3fe9..a2704a0f944 100644 --- a/pkg/instance/stop.go +++ b/pkg/instance/stop.go @@ -131,8 +131,7 @@ func StopForcibly(inst *store.Instance) { logrus.Info("The host agent process seems already stopped") } - suffixesToBeRemoved := []string{".pid", ".sock", ".tmp"} - globPatterns := strings.ReplaceAll(strings.Join(suffixesToBeRemoved, " "), ".", "*.") + globPatterns := strings.ReplaceAll(strings.Join(filenames.TmpFileSuffixes, " "), ".", "*.") logrus.Infof("Removing %s under %q", globPatterns, inst.Dir) fi, err := os.ReadDir(inst.Dir) @@ -142,7 +141,7 @@ func StopForcibly(inst *store.Instance) { } for _, f := range fi { path := filepath.Join(inst.Dir, f.Name()) - for _, suffix := range suffixesToBeRemoved { + for _, suffix := range filenames.TmpFileSuffixes { if strings.HasSuffix(path, suffix) { logrus.Infof("Removing %q", path) if err := os.Remove(path); err != nil { diff --git a/pkg/store/filenames/filenames.go b/pkg/store/filenames/filenames.go index cfb825fc0ee..cc9a0fc9f33 100644 --- a/pkg/store/filenames/filenames.go +++ b/pkg/store/filenames/filenames.go @@ -91,3 +91,17 @@ const LongestSock = SSHSock + ".1234567890123456" func PIDFile(name string) string { return name + ".pid" } + +// SkipOnClone files should be skipped on cloning an instance. +var SkipOnClone = []string{ + Protected, +} + +// NullifyOnClone files should be nullified on cloning an instance. +// FIXME: this list should be provided by the VM driver. +var NullifyOnClone = []string{ + VzIdentifier, +} + +// TmpFileSuffixes is the list of the tmp file suffixes. +var TmpFileSuffixes = []string{".pid", ".sock", ".tmp"} From c2a26c1ec2855bf8131608cbd277252a81ce5f76 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 8 Jul 2025 16:58:33 +0900 Subject: [PATCH 82/91] CI: unify Windows runners to `windows-2025` Different windows runners were used across QEMU, WSL2, and linters Signed-off-by: Akihiro Suda --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49ad89019f2..2da2b42be72 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,7 +72,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - runs-on: [ubuntu-24.04, macos-15, windows-2022] + runs-on: [ubuntu-24.04, macos-15, windows-2025] runs-on: ${{ matrix.runs-on }} steps: - name: Force git to use LF @@ -150,7 +150,7 @@ jobs: windows: name: "Windows tests (WSL2)" - runs-on: windows-2025-8-cores + runs-on: windows-2025 timeout-minutes: 30 steps: - name: Enable WSL2 From 906c157b3ccb58760870cf008d78b94baad950e6 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 8 Jul 2025 18:51:52 +0900 Subject: [PATCH 83/91] templates: update Signed-off-by: Akihiro Suda --- templates/_images/almalinux-9.yaml | 28 +++++++++++-------------- templates/_images/alpine.yaml | 13 ++++++------ templates/_images/archlinux.yaml | 4 ++-- templates/_images/centos-stream-10.yaml | 16 +++++++------- templates/_images/centos-stream-9.yaml | 16 +++++++------- templates/_images/debian-11.yaml | 12 +++++------ templates/_images/debian-12.yaml | 12 +++++------ templates/_images/ubuntu-20.04.yaml | 20 +++++++++--------- templates/_images/ubuntu-22.04.yaml | 24 ++++++++++----------- templates/_images/ubuntu-24.04.yaml | 24 ++++++++++----------- templates/_images/ubuntu-24.10.yaml | 24 ++++++++++----------- templates/_images/ubuntu-25.04.yaml | 25 +++++++++++----------- 12 files changed, 106 insertions(+), 112 deletions(-) diff --git a/templates/_images/almalinux-9.yaml b/templates/_images/almalinux-9.yaml index 313ab38356a..dcc7632b8b8 100644 --- a/templates/_images/almalinux-9.yaml +++ b/templates/_images/almalinux-9.yaml @@ -1,20 +1,16 @@ images: -- location: https://repo.almalinux.org/almalinux/9.5/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.5-20241120.x86_64.qcow2 - arch: x86_64 - digest: sha256:abddf01589d46c841f718cec239392924a03b34c4fe84929af5d543c50e37e37 - -- location: https://repo.almalinux.org/almalinux/9.5/cloud/aarch64/images/AlmaLinux-9-GenericCloud-9.5-20241120.aarch64.qcow2 - arch: aarch64 - digest: sha256:5ede4affaad0a997a2b642f1628b6268bd8eba775f281346b75be3ed20ec369e - -- location: https://repo.almalinux.org/almalinux/9.5/cloud/s390x/images/AlmaLinux-9-GenericCloud-9.5-20241120.s390x.qcow2 - arch: s390x - digest: sha256:9948ea1c5ee1c6497bde5cec18c71c5f9e50861af4c0f1400c1af504eee2b995 - -- location: https://repo.almalinux.org/almalinux/9.5/cloud/ppc64le/images/AlmaLinux-9-GenericCloud-9.5-20241120.ppc64le.qcow2 - arch: ppc64le - digest: sha256:24feab47c4a89f7d3bda58001dde59ea74904b25edbb68a207f39277c0772522 - +- location: "https://repo.almalinux.org/almalinux/9.6/cloud/x86_64/images/AlmaLinux-9-GenericCloud-9.6-20250522.x86_64.qcow2" + arch: "x86_64" + digest: "sha256:b08cd5db79bf32860412f5837e8c7b8df9447e032376e3c622840b31aaf26bc6" +- location: "https://repo.almalinux.org/almalinux/9.6/cloud/aarch64/images/AlmaLinux-9-GenericCloud-9.6-20250522.aarch64.qcow2" + arch: "aarch64" + digest: "sha256:47e6801d066c311c44a5ce8100bed16b5976bde610e599dd384d1dca73b31ac5" +- location: "https://repo.almalinux.org/almalinux/9.6/cloud/s390x/images/AlmaLinux-9-GenericCloud-9.6-20250522.s390x.qcow2" + arch: "s390x" + digest: "sha256:81f5b96794668d6b7009d4edcd00980b6043fc4b7bd954ffa50dd74ebccb1b44" +- location: "https://repo.almalinux.org/almalinux/9.6/cloud/ppc64le/images/AlmaLinux-9-GenericCloud-9.6-20250522.ppc64le.qcow2" + arch: "ppc64le" + digest: "sha256:5334d70261680a7f52c98fcb414f3c82dcefb65909bf02ec604491b7fd57eb2a" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/alpine.yaml b/templates/_images/alpine.yaml index 4c1403334a5..04a988991c6 100644 --- a/templates/_images/alpine.yaml +++ b/templates/_images/alpine.yaml @@ -1,8 +1,7 @@ images: -- location: https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/cloud/nocloud_alpine-3.21.2-x86_64-uefi-cloudinit-r0.qcow2 - arch: x86_64 - digest: sha512:1aaf22b4a584e69e228e6aa38a295159c0143d9ccebe7ad4928e92b414714066af3bfe5f9e0ca4d4d64a70ca9fea09033af90258a6f2344130d70b660151127a - -- location: https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/cloud/nocloud_alpine-3.21.2-aarch64-uefi-cloudinit-r0.qcow2 - arch: aarch64 - digest: sha512:08d340126b222abae651a20aa63c3ee3dc601d703de7879d2a6bc1fe82a3664d058a2c55ad0cf8a874327f7535e3af8a9384ce438217d6f32200cad1462a5b32 +- location: "https://dl-cdn.alpinelinux.org/alpine/v3.22/releases/cloud/nocloud_alpine-3.22.0-x86_64-uefi-cloudinit-r0.qcow2" + arch: "x86_64" + digest: "sha512:2ebfc0d515dee0b8a0732d77c99f050bf2a413a5d6bc3634ac94cb48f7b31a3e59431f732810edccf4b39cc0045275a947496aff534804f2cac6e7e9d63c7c74" +- location: "https://dl-cdn.alpinelinux.org/alpine/v3.22/releases/cloud/nocloud_alpine-3.22.0-aarch64-uefi-cloudinit-r0.qcow2" + arch: "aarch64" + digest: "sha512:30b347397387926eeb939d93c926e09833f5b49c6c6de5cc225ccdfe6e54aba88251c71da264c7e4260e78132b50e34b93409c8b4da2e843e68a4dc35fc6b155" diff --git a/templates/_images/archlinux.yaml b/templates/_images/archlinux.yaml index 346368961ee..64f73a8311d 100644 --- a/templates/_images/archlinux.yaml +++ b/templates/_images/archlinux.yaml @@ -1,9 +1,9 @@ images: # Try to use yyyyMMdd.REV image if available. Note that yyyyMMdd.REV will be removed after several months. -- location: "https://geo.mirror.pkgbuild.com/images/v20250415.336224/Arch-Linux-x86_64-cloudimg-20250415.336224.qcow2" +- location: "https://geo.mirror.pkgbuild.com/images/v20250701.374901/Arch-Linux-x86_64-cloudimg-20250701.374901.qcow2" arch: "x86_64" - digest: "sha256:f9086324b53fb4c50ead595e92c5addf2bb28940506be8b861d400760565c344" + digest: "sha256:f9a1968e2d3c4808dc52737c9ef04d666c8cf9f66d438e47162d8dac5bcfadb3" - location: https://github.com/mcginty/arch-boxes-arm/releases/download/v20220323/Arch-Linux-aarch64-cloudimg-20220323.0.qcow2 arch: aarch64 digest: sha512:27524910bf41cb9b3223c8749c6e67fd2f2fdb8b70d40648708e64d6b03c0b4a01b3c5e72d51fefd3e0c3f58487dbb400a79ca378cde2da341a3a19873612be8 diff --git a/templates/_images/centos-stream-10.yaml b/templates/_images/centos-stream-10.yaml index 640374d54c0..dae9199c371 100644 --- a/templates/_images/centos-stream-10.yaml +++ b/templates/_images/centos-stream-10.yaml @@ -1,18 +1,18 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-20250520.0.x86_64.qcow2" +- location: "https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-10-20250617.0.x86_64.qcow2" arch: "x86_64" - digest: "sha256:16b7ef3db3fe1ac60d8140d34f1bf7b24dc1636e7ad2412f909232f014a2875d" -- location: "https://cloud.centos.org/centos/10-stream/aarch64/images/CentOS-Stream-GenericCloud-10-20250520.0.aarch64.qcow2" + digest: "sha256:ac8b26eba30abb9b129789b8f045dbc40c620ff6227bdfc0af6a74f11633b0f5" +- location: "https://cloud.centos.org/centos/10-stream/aarch64/images/CentOS-Stream-GenericCloud-10-20250617.0.aarch64.qcow2" arch: "aarch64" - digest: "sha256:814d7887011c102b3c5fe55e2a5ba4966d89e7c26a38da1e7277719861aeba0a" -- location: "https://cloud.centos.org/centos/10-stream/s390x/images/CentOS-Stream-GenericCloud-10-20250520.0.s390x.qcow2" + digest: "sha256:d1b7496ef06370156f5d9de86d67b45de307c935bc6a31444b828ad51c404211" +- location: "https://cloud.centos.org/centos/10-stream/s390x/images/CentOS-Stream-GenericCloud-10-20250617.0.s390x.qcow2" arch: "s390x" - digest: "sha256:8d4597d8117abe216beb30e3b52bb2b5e4e405544151055e139d77d05b37d423" -- location: "https://cloud.centos.org/centos/10-stream/ppc64le/images/CentOS-Stream-GenericCloud-10-20250520.0.ppc64le.qcow2" + digest: "sha256:98bc4704dacf8bef7b2cf8ecbdb56062308383b5efbdf3b83c80b91e726ee070" +- location: "https://cloud.centos.org/centos/10-stream/ppc64le/images/CentOS-Stream-GenericCloud-10-20250617.0.ppc64le.qcow2" arch: "ppc64le" - digest: "sha256:88ad4ba818dfd0f43e5fff9d4a793589df4f35c5120a9cf3958960d765662ca9" + digest: "sha256:e7cd921c57eb14c4297c6cacab0d28f428aac029b6aa05725309ed10fb6c2602" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/centos-stream-9.yaml b/templates/_images/centos-stream-9.yaml index 7947ef10966..d3db80269e9 100644 --- a/templates/_images/centos-stream-9.yaml +++ b/templates/_images/centos-stream-9.yaml @@ -1,18 +1,18 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20250512.0.x86_64.qcow2" +- location: "https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20250617.0.x86_64.qcow2" arch: "x86_64" - digest: "sha256:9c7cbce3c1fdd1e06e4fea8d577ec014f18eb700e48629b04a84ce1589890135" -- location: "https://cloud.centos.org/centos/9-stream/aarch64/images/CentOS-Stream-GenericCloud-9-20250512.0.aarch64.qcow2" + digest: "sha256:6aac7ec8736b19347877c2c401547ce9ab306d8d120d741823bd1fa0d365b150" +- location: "https://cloud.centos.org/centos/9-stream/aarch64/images/CentOS-Stream-GenericCloud-9-20250617.0.aarch64.qcow2" arch: "aarch64" - digest: "sha256:583aff93ae902f5085aa2bc44f648fd63d5fa706285823bd9bbbaf4fc40070dd" -- location: "https://cloud.centos.org/centos/9-stream/s390x/images/CentOS-Stream-GenericCloud-9-20250512.0.s390x.qcow2" + digest: "sha256:bb89c93f6de4766221bf01f41193c6d1d1dec39cf1ca197c8ac12b9dcc25864f" +- location: "https://cloud.centos.org/centos/9-stream/s390x/images/CentOS-Stream-GenericCloud-9-20250617.0.s390x.qcow2" arch: "s390x" - digest: "sha256:4788b2bb31c9014c0eb09f8f058ae88ef5956df058e553c86ef0dcab044d334a" -- location: "https://cloud.centos.org/centos/9-stream/ppc64le/images/CentOS-Stream-GenericCloud-9-20250512.0.ppc64le.qcow2" + digest: "sha256:20e569b105fdd5133c840f901bdc27a7a01451f356464010789ca11941fc81c9" +- location: "https://cloud.centos.org/centos/9-stream/ppc64le/images/CentOS-Stream-GenericCloud-9-20250617.0.ppc64le.qcow2" arch: "ppc64le" - digest: "sha256:0e1886f61015998abb0dcc691238b28bf36933be27e7bee5a4a4df3b5fdf8844" + digest: "sha256:b9d38a317b18b7975aa2664f65e2a42a0a1bbce2575bf1f1a63d497aa9a9b814" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/debian-11.yaml b/templates/_images/debian-11.yaml index 44898dd0a23..640bf2837c3 100644 --- a/templates/_images/debian-11.yaml +++ b/templates/_images/debian-11.yaml @@ -1,15 +1,15 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud.debian.org/images/cloud/bullseye/20250512-2110/debian-11-genericcloud-amd64-20250512-2110.qcow2" +- location: "https://cloud.debian.org/images/cloud/bullseye/20250703-2162/debian-11-genericcloud-amd64-20250703-2162.qcow2" arch: "x86_64" - digest: "sha512:c902c738e05c0a9d67284563996aaae82a27b7df5240f38620ef9a7bdcab82e82317fa2209861718e74952199400607b30d8bd5c4a5cf53823ce4f54deb5d895" -- location: "https://cloud.debian.org/images/cloud/bullseye/20250512-2110/debian-11-genericcloud-arm64-20250512-2110.qcow2" + digest: "sha512:e60b2201a9924a23cfd520cc9e5d9424330240d41b5f5be1d7c6962d649d849c7df5ab69036d707a4e211d4b1171a8110eaeffaaefc7001f83dd403dd9dece5b" +- location: "https://cloud.debian.org/images/cloud/bullseye/20250703-2162/debian-11-genericcloud-arm64-20250703-2162.qcow2" arch: "aarch64" - digest: "sha512:e79761b03da4928ce6251236a739d2ac33c183c0b7e8d5474ec7218c3e4296b11bb81f10d54973c67d49535500898bb9ca3a984ca96f0314a2d19face956021f" -- location: "https://cloud.debian.org/images/cloud/bullseye/20250512-2110/debian-11-genericcloud-ppc64el-20250512-2110.qcow2" + digest: "sha512:6830c6555971a28b01a54240ea91710b81569a9abe91569fc9ab36fc8552f100be975762299543765651bce4219bc52f2b28c10a318cbcd7ae04644d0ec3d29e" +- location: "https://cloud.debian.org/images/cloud/bullseye/20250703-2162/debian-11-genericcloud-ppc64el-20250703-2162.qcow2" arch: "ppc64le" - digest: "sha512:d0eb263160c14242b47a008d43295bb59d2ced056849000311d52e9b9fa820d13a8387234faf54bab4f43040d5b044e4bdc80e8fc5e55ba5e03a878356b4151a" + digest: "sha512:71f0039d3ae46504d189f009f91fb9fa9873153d250969b56a4656d75caec17356705237d7926153e2c10d9251dc4909ec34a2afa7256a9359df279cbde8d679" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/debian-12.yaml b/templates/_images/debian-12.yaml index 978979500bf..215c6751ca3 100644 --- a/templates/_images/debian-12.yaml +++ b/templates/_images/debian-12.yaml @@ -1,15 +1,15 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud.debian.org/images/cloud/bookworm/20250519-2117/debian-12-genericcloud-amd64-20250519-2117.qcow2" +- location: "https://cloud.debian.org/images/cloud/bookworm/20250703-2162/debian-12-genericcloud-amd64-20250703-2162.qcow2" arch: "x86_64" - digest: "sha512:ee35c87b8a0c67eb67a169beec3bef5d6868957c1d31374797ef37072650c5067094c50ca293d7b926a967d07515abf1845f515084e3a192c7c69df75e84c95a" -- location: "https://cloud.debian.org/images/cloud/bookworm/20250519-2117/debian-12-genericcloud-arm64-20250519-2117.qcow2" + digest: "sha512:da702efced2cd98017790d0e00fee81f1e1404d3f990a4741f52e6f18bde9856d37799c053b3baa48805048a595d2a6a13c41b8287ec6f76ec27b7ef1b67a215" +- location: "https://cloud.debian.org/images/cloud/bookworm/20250703-2162/debian-12-genericcloud-arm64-20250703-2162.qcow2" arch: "aarch64" - digest: "sha512:9282da6c8509e16191a3950c63174a4c94f8921ff2062ed54912f86aa8c986edf443d7f77f0e9d0937c2576f09fbacb2fd53459875ec2c7afb015c70d54c7e19" -- location: "https://cloud.debian.org/images/cloud/bookworm/20250519-2117/debian-12-genericcloud-ppc64el-20250519-2117.qcow2" + digest: "sha512:95f72a3dbc9bbe261aee622ffe0c1b89085d843c9631b720738557a3640e229847deee7f0f87a29b566bf56ba7ecdc9010fb4b844c3477ca76a00865cf016fc1" +- location: "https://cloud.debian.org/images/cloud/bookworm/20250703-2162/debian-12-genericcloud-ppc64el-20250703-2162.qcow2" arch: "ppc64le" - digest: "sha512:67dcadf2975455a2e3b32bf4025f1b2429b7b189a34db69214168f63ef977704a4eb03f3a28ae4a0f3799374ed302327fe6628e352c8043570f45b8f9fe9b88e" + digest: "sha512:fcf25e5d0e6a76e93c3abaecb8109a931380af29658586f51e30baec0f699a2f17de8a14de57acec80b13ab125fd165140893d814800144505a4982492256f19" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/ubuntu-20.04.yaml b/templates/_images/ubuntu-20.04.yaml index 04e9fd73e09..bd05aae94ad 100644 --- a/templates/_images/ubuntu-20.04.yaml +++ b/templates/_images/ubuntu-20.04.yaml @@ -1,21 +1,21 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250516/ubuntu-20.04-server-cloudimg-amd64.img" +- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250624/ubuntu-20.04-server-cloudimg-amd64.img" arch: "x86_64" - digest: "sha256:3d5a77f8f6a240cd3478c24cc4d0a11a823236b03868d79a432906c394b835a9" -- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250516/ubuntu-20.04-server-cloudimg-arm64.img" + digest: "sha256:18f2977d77dfea1b74aee14533bd21c34f789139e949c57023b7364894b7e5e9" +- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250624/ubuntu-20.04-server-cloudimg-arm64.img" arch: "aarch64" - digest: "sha256:c1fa8013924407283fbc25af7d50cb3cfd1811d7099bdb624bf699d91b3f1a41" -- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250516/ubuntu-20.04-server-cloudimg-armhf.img" + digest: "sha256:d4603cd783e2578f838c63f7c3a8dae6b554bd6358870f2c8eb182aa51ade465" +- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250624/ubuntu-20.04-server-cloudimg-armhf.img" arch: "armv7l" - digest: "sha256:709e58073d548ba4515c012314eda9528c73986b7f6f88b02becbe3daae9000d" -- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250516/ubuntu-20.04-server-cloudimg-s390x.img" + digest: "sha256:5dba684cb4d143b0f7105c21449155cbf9b8c77505d09a96c7ff104ea76bc943" +- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250624/ubuntu-20.04-server-cloudimg-s390x.img" arch: "s390x" - digest: "sha256:fc6f187f301e4c2e35c01f106efe9c528c5ca2da50faafc0e0c586d8b8e368ff" -- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250516/ubuntu-20.04-server-cloudimg-ppc64el.img" + digest: "sha256:48e42f4dc1e4f539b0a1a78078a6ffb4fa48d9ea4bb8a7086d5e33af9452122a" +- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250624/ubuntu-20.04-server-cloudimg-ppc64el.img" arch: "ppc64le" - digest: "sha256:560d8b1532efb87bb1f105ac251428e789284cca3080d3d55bbce87982370cc2" + digest: "sha256:47c32128a4c96985e2a44a8b8539c560ab06cd26e1276fe73502fc4ff8849e18" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/ubuntu-22.04.yaml b/templates/_images/ubuntu-22.04.yaml index 934a32ebcf4..d5a4242aa37 100644 --- a/templates/_images/ubuntu-22.04.yaml +++ b/templates/_images/ubuntu-22.04.yaml @@ -1,24 +1,24 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-amd64.img" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-amd64.img" arch: "x86_64" - digest: "sha256:a037d6d0299171ba62019f3c3f895eb902c271f8e9676cfe0f22e38070344579" -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-arm64.img" + digest: "sha256:80232fb756d0ba69d3ff4b0f717362d7cb24f55a5f1b4f63e9e09c7f6bed99d2" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-arm64.img" arch: "aarch64" - digest: "sha256:a83795b1ff39e13314e31001bf358081be6e7f7eb3ae93a72cc494ceef51462e" -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-riscv64.img" + digest: "sha256:7b86a56f8069b7f7ea20dcaa9a9711a03db0bd37118c632474474338792613b6" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-riscv64.img" arch: "riscv64" - digest: "sha256:b7f9eb9046a80d34f9b61de2a9c1c5869d204ed9250862dea8bb2f25df7ccc51" -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-armhf.img" + digest: "sha256:abcb32e3b22ba819e13e06af193214b99112a3308595c1715196f9bd86777ce6" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-armhf.img" arch: "armv7l" - digest: "sha256:e0e4755938b3234f229dad40e5969b520396cc1a31be1ec82104ec9c568ce055" -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-s390x.img" + digest: "sha256:80328e8f238b6a7efb2a07cae8e585728820968ed5ae4f654c53eef2942ba0db" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-s390x.img" arch: "s390x" - digest: "sha256:5a55bb49213b093b03dd54b4e4cc3b873f6b39a05ab25480d246bb99b1fa4d4a" -- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250516/ubuntu-22.04-server-cloudimg-ppc64el.img" + digest: "sha256:7c71c8e51ca17f67014326873ac2f0bf3b9908d1be75067957e60a4862464dd6" +- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250702/ubuntu-22.04-server-cloudimg-ppc64el.img" arch: "ppc64le" - digest: "sha256:923c250246b42aae1213be3b5df35d866ec5e2b6f7aebefa2095109fd8c1d575" + digest: "sha256:a1037c2fb24c6211b48b084e824f7ba927ff84820ef81b15d00f8db267e9c801" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/ubuntu-24.04.yaml b/templates/_images/ubuntu-24.04.yaml index b78d60a9a4b..eda0f06b21d 100644 --- a/templates/_images/ubuntu-24.04.yaml +++ b/templates/_images/ubuntu-24.04.yaml @@ -1,24 +1,24 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-amd64.img" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-amd64.img" arch: "x86_64" - digest: "sha256:8d6161defd323d24d66f85dda40e64e2b9021aefa4ca879dcbc4ec775ad1bbc5" -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-arm64.img" + digest: "sha256:f1652d29d497fb7c623433705c9fca6525d1311b11294a0f495eed55c7639d1f" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-arm64.img" arch: "aarch64" - digest: "sha256:c933c6932615d26c15f6e408e4b4f8c43cb3e1f73b0a98c2efa916cc9ab9549c" -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-riscv64.img" + digest: "sha256:bbecbb88100ee65497927ed0da247ba15af576a8855004182cf3c87265e25d35" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-riscv64.img" arch: "riscv64" - digest: "sha256:8b7f41b6530609a5cb1c4ec21b1993e6e80985e5c9c2b13d43dad95bc58afb94" -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-armhf.img" + digest: "sha256:3694a05d426568b8a8d7c8ac57cf55d5b1b1fc03ece48835bdd53a51874a063c" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-armhf.img" arch: "armv7l" - digest: "sha256:c9a9c7d6cd680b4faf8421da78e64aab7a3d024e86d48e57472fe74a4f45343e" -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-s390x.img" + digest: "sha256:e2e1780a3785482887ee61b05019a072c343e4dcff5fa5239e3a0696d44d117b" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-s390x.img" arch: "s390x" - digest: "sha256:ecf500c278ed43dddc80edaa68f24e4e8bbe7e715169f97347b82991054ddbea" -- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250516/ubuntu-24.04-server-cloudimg-ppc64el.img" + digest: "sha256:f05c9c41ae23e82340799b0d04a0959b9e71e16526cd91f55188f531ce190c21" +- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-ppc64el.img" arch: "ppc64le" - digest: "sha256:11fc8af103fa6ae0f9b250e7929bf6fba15cb9fb3434108f699569e918c8eba5" + digest: "sha256:7dc45587dac45028d022eaa56e102eca165dfd4b73fa294f8f2c447da4cc251a" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/ubuntu-24.10.yaml b/templates/_images/ubuntu-24.10.yaml index 827c8d9da5e..24796678ad8 100644 --- a/templates/_images/ubuntu-24.10.yaml +++ b/templates/_images/ubuntu-24.10.yaml @@ -1,24 +1,24 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-amd64.img" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-amd64.img" arch: "x86_64" - digest: "sha256:ad138a12a105ba1d6138fee0a7827de9edb04c8e3497c4ee03bfb00222e47668" -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-arm64.img" + digest: "sha256:69f31d3208895e5f646e345fbc95190e5e311ecd1359a4d6ee2c0b6483ceca03" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-arm64.img" arch: "aarch64" - digest: "sha256:ed3eb300e553f30b6d7ba5a18c9c654848e706e9701eb904e46a374f204d2e89" -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-riscv64.img" + digest: "sha256:3d1d134d66318f982d32f02aec00fe879bfeb0338147b4038a25d1f9cddb527f" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-riscv64.img" arch: "riscv64" - digest: "sha256:6879c1d7eefe03ac061647ea96d189608e5cfb18e9c2432bbb91dbc2c74913fc" -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-armhf.img" + digest: "sha256:0803a88c98fe7f511f379ce378751ce4f435e24da756540583b5dc269116d83a" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-armhf.img" arch: "armv7l" - digest: "sha256:248d6d6fbe0ac73448f7ec984db2ecfce89602767634d09b6d631e7fa3286047" -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-s390x.img" + digest: "sha256:818741e7c423248fdc18d503beea094acad9ed1710ba14e5df3fae54f2a76b72" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-s390x.img" arch: "s390x" - digest: "sha256:84d6f5289747170a5b0e405e82fd947bafb9e40dfe0ac858ba4b81f8e5b61a2d" -- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250502/ubuntu-24.10-server-cloudimg-ppc64el.img" + digest: "sha256:ae5bd8d899b6b9908c76b0416a3e82e899654c6048d0eea8e18159a51e01b774" +- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-ppc64el.img" arch: "ppc64le" - digest: "sha256:063768391a5f9353d1c2f24479f762f3e0abac35a86e9b93a77db03b519381e6" + digest: "sha256:e78b9b8df911926de80f4fbeb9465256c77ac1da7c1b058d236b603fc4eb755f" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache diff --git a/templates/_images/ubuntu-25.04.yaml b/templates/_images/ubuntu-25.04.yaml index 60adc9ccc87..fbaec7bb695 100644 --- a/templates/_images/ubuntu-25.04.yaml +++ b/templates/_images/ubuntu-25.04.yaml @@ -1,25 +1,24 @@ images: # Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months. -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-amd64.img" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-amd64.img" arch: "x86_64" - digest: "sha256:ee752a88573fc8347b4082657293da54a7ba301e3d83cc935fedb4ab6d7129e2" -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-arm64.img" + digest: "sha256:92ad8714338b1d3be605b7981137a7c356b23527ff1aba8e80cf44bc3aa2f8da" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-arm64.img" arch: "aarch64" - digest: "sha256:9594596f24b6b47aeda06328a79c4626cdb279c3490e05ac1a9113c19c8f161b" -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-riscv64.img" + digest: "sha256:26d0ac2236f12954923eb35ddfee8fa9fff3eab6111ba84786b98ab3b972c6d8" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-riscv64.img" arch: "riscv64" - digest: "sha256:e4a83c1b4896726e765e0c29c1abcfe1aea62d564501ff8e4757b46c233d1cc9" -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-armhf.img" + digest: "sha256:66f420cb992e3076207ce933b7f96550d27b05ead561b7a5e48d281d01b151de" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-armhf.img" arch: "armv7l" - digest: "sha256:69849590334a9dfec8def97a0bcb54744f136dca9eaac8d011dca22e84058c41" -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-s390x.img" + digest: "sha256:f37d82f55df80bdb09bbeb403f84e6921b8e4435610fed22ffec436af2e4dd6d" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-s390x.img" arch: "s390x" - digest: "sha256:34020c9ced44598cd0d8ea77527aad61db4498f5151fac9fafdd1199cddf8919" -- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-ppc64el.img" + digest: "sha256:1d03fedf3aea85438ce70d745edf611b80c6f88a049bb44167c24ae2ec9d4fda" +- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250701/ubuntu-25.04-server-cloudimg-ppc64el.img" arch: "ppc64le" - digest: "sha256:d1190f1db8eb6602d3f7de35f6f425fb671bb9ed953df821c0d1cc8cedbaa2ef" - + digest: "sha256:5922f8ea7958f15af5a5d8a4efec38d66af31c0a853fba5fb0790ec25562b53a" # Fallback to the latest release image. # Hint: run `limactl prune` to invalidate the cache From 644a8a9dabc52cec3ccc772e7a68f8ff7daddbf3 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 14:16:07 +0900 Subject: [PATCH 84/91] Kconfig: deprecate Will be removed in Lima v2.0. See issue 3639 Signed-off-by: Akihiro Suda --- Kconfig | 2 ++ Makefile | 2 ++ .../content/en/docs/installation/source.md | 26 ------------------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/Kconfig b/Kconfig index 7e40d787590..3828b11a5df 100644 --- a/Kconfig +++ b/Kconfig @@ -1,3 +1,5 @@ +# Kconfig is deprecated since Lima v1.2, and will be removed in Lima v2.0. +# Use Makefile variables instead. mainmenu "Lima" diff --git a/Makefile b/Makefile index c9da565c716..4015ed391fd 100644 --- a/Makefile +++ b/Makefile @@ -145,9 +145,11 @@ native: clean limactl helpers native-guestagent templates template_experimentals ################################################################################ # Kconfig config: Kconfig + $(warning Kconfig is deprecated since Lima v1.2 and will be removed in Lima v2.0.) $(KCONFIG_CONF) $< menuconfig: Kconfig + $(warning Kconfig is deprecated since Lima v1.2 and will be removed in Lima v2.0.) MENUCONFIG_STYLE=aquatic \ $(KCONFIG_MCONF) $< diff --git a/website/content/en/docs/installation/source.md b/website/content/en/docs/installation/source.md index 2c23b28858d..e887b5d8489 100644 --- a/website/content/en/docs/installation/source.md +++ b/website/content/en/docs/installation/source.md @@ -51,29 +51,3 @@ tar czf lima-additional-guestagents-package.tar.gz * ``` These packages can then be transferred and installed on the target system. - -## Advanced Configuration with Kconfig Tools -(This step is not needed for most users) - -To change the build configuration such as the guest architectures, run: - -```bash -make config # For text-based configuration -make menuconfig # For a menu-based configuration -``` - -This requires Kconfig tools to be installed. It is also possible to manually edit `.config`. The default configuration can be found in `config.mk` (which follows make syntax). - -The tools are available as either `kconfig-frontends` or `kbuild-standalone`. There are two interfaces: -- `conf` for text-based configuration. -- `mconf` for a menu-driven interface. - -A Python implementation is available at [Kconfiglib](https://pypi.org/project/kconfiglib). It can be installed with: - -```bash -pip install --user kconfiglib -``` - -This also includes support for `guiconfig` (GUI-based configuration). - - From 6c106d19627ee7c944ad12018495b0dffbf353a0 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 14:32:37 +0900 Subject: [PATCH 85/91] Fix kernel decompression Fix issue 3705 Signed-off-by: Akihiro Suda --- pkg/instance/start.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/instance/start.go b/pkg/instance/start.go index d0c344fc8d7..e465fa2755f 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -117,7 +117,8 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) { continue } if f.Kernel != nil { - if _, err := fileutils.DownloadFile(ctx, kernel, f.Kernel.File, false, "the kernel", *inst.Config.Arch); err != nil { + // ensure decompress kernel because vz expects it to be decompressed + if _, err := fileutils.DownloadFile(ctx, kernel, f.Kernel.File, true, "the kernel", *inst.Config.Arch); err != nil { errs[i] = err continue } @@ -129,6 +130,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) { } } if f.Initrd != nil { + // vz does not need initrd to be decompressed if _, err := fileutils.DownloadFile(ctx, initrd, *f.Initrd, false, "the initrd", *inst.Config.Arch); err != nil { errs[i] = err continue From a78393c1279f53d47cb8ee8e66dbdd5a0a338b51 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 17:26:04 +0900 Subject: [PATCH 86/91] De-deprecate LIMA_SSH_PORT_FORWARDER It should be still legit to prefer the legacy stable implementation Signed-off-by: Akihiro Suda --- pkg/hostagent/hostagent.go | 3 +-- website/content/en/docs/config/environment-variables.md | 3 +-- website/content/en/docs/releases/deprecated.md | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index 7ae27aaa6dd..8b0eceab09a 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -53,7 +53,7 @@ type HostAgent struct { instName string instSSHAddress string sshConfig *ssh.SSHConfig - portForwarder *portForwarder // legacy SSH port forwarder (deprecated) + portForwarder *portForwarder // legacy SSH port forwarder grpcPortForwarder *portfwd.Forwarder onClose []func() error // LIFO @@ -660,7 +660,6 @@ func (a *HostAgent) processGuestAgentEvents(ctx context.Context, client *guestag } } if useSSHFwd { - logrus.Warn("LIMA_SSH_PORT_FORWARDER is deprecated") a.portForwarder.OnEvent(ctx, ev) } else { a.grpcPortForwarder.OnEvent(ctx, client, ev) diff --git a/website/content/en/docs/config/environment-variables.md b/website/content/en/docs/config/environment-variables.md index 08f704dc970..dcaa128c278 100644 --- a/website/content/en/docs/config/environment-variables.md +++ b/website/content/en/docs/config/environment-variables.md @@ -75,7 +75,6 @@ This page documents the environment variables used in Lima. ```sh export LIMA_SSH_PORT_FORWARDER=false ``` -- **Note**: Deprecated since v1.1. It is expected that this variable will be removed in future. - **The history of the default value**: | Version | Default value | |---------------|---------------------| @@ -170,4 +169,4 @@ This page documents the environment variables used in Lima. - **Usage**: ```sh export QEMU_SYSTEM_X86_64=/usr/local/bin/qemu-system-x86_64 - ``` \ No newline at end of file + ``` diff --git a/website/content/en/docs/releases/deprecated.md b/website/content/en/docs/releases/deprecated.md index 7a108b5a89e..23c5956d612 100644 --- a/website/content/en/docs/releases/deprecated.md +++ b/website/content/en/docs/releases/deprecated.md @@ -8,7 +8,6 @@ The following features are deprecated: - CentOS 7 support - Loading non-strict YAMLs (i.e., YAMLs with unknown properties) - `limactl show-ssh` command (Use `ssh -F ~/.lima/default/ssh.config lima-default` instead) -- `LIMA_SSH_PORT_FORWARDER=true` (since Lima v1.1) - Ansible provisioning mode (Use `ansible-playbook playbook.yaml` after the start instead) ## Removed features From 2dc4103ed2e9791b1e5c1b71cfc33f4e545d9578 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 17:36:23 +0900 Subject: [PATCH 87/91] editflags: fix shell completion for containerd Signed-off-by: Akihiro Suda --- cmd/limactl/editflags/editflags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/limactl/editflags/editflags.go b/cmd/limactl/editflags/editflags.go index 9f4e7c601af..4b3d7c1df60 100644 --- a/cmd/limactl/editflags/editflags.go +++ b/cmd/limactl/editflags/editflags.go @@ -82,7 +82,7 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) { }) flags.String("containerd", "", commentPrefix+"containerd mode (user, system, user+system, none)") - _ = cmd.RegisterFlagCompletionFunc("vm-type", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + _ = cmd.RegisterFlagCompletionFunc("containerd", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return []string{"user", "system", "user+system", "none"}, cobra.ShellCompDirectiveNoFileComp }) From 58ad3053b26b051815c0f725f8daf19aad1223a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 01:03:53 +0000 Subject: [PATCH 88/91] build(deps): bump the golang-x group with 3 updates --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-version: 0.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/sys dependency-version: 0.34.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/text dependency-version: 0.27.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 3747bc3217f..9efd8dd5b3c 100644 --- a/go.mod +++ b/go.mod @@ -42,9 +42,9 @@ require ( github.com/spf13/pflag v1.0.6 github.com/wk8/go-ordered-map/v2 v2.1.8 golang.org/x/net v0.41.0 - golang.org/x/sync v0.15.0 - golang.org/x/sys v0.33.0 // gomodjail:unconfined - golang.org/x/text v0.26.0 + golang.org/x/sync v0.16.0 + golang.org/x/sys v0.34.0 // gomodjail:unconfined + golang.org/x/text v0.27.0 google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 // gomodjail:unconfined gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 @@ -120,7 +120,7 @@ require ( golang.org/x/oauth2 v0.28.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 2f211757334..2646c22034b 100644 --- a/go.sum +++ b/go.sum @@ -335,8 +335,8 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -360,8 +360,8 @@ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -385,8 +385,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -400,8 +400,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c41880fe4a20461e808b05dccfad62521706d30c Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 17:12:23 +0900 Subject: [PATCH 89/91] CI: test portfwd issue (`w3m -dump` hangs) Test for issue 3685 Signed-off-by: Akihiro Suda --- .github/workflows/test.yml | 9 +++++---- hack/test-templates.sh | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2da2b42be72..da61fd8025a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -264,7 +264,8 @@ jobs: # QEMU: required by Lima itself # bash: required by test-templates.sh (OS version of bash is too old) # coreutils: required by test-templates.sh for the "timeout" command - run: brew install qemu bash coreutils + # w3m : required by test-templates.sh for port forwarding tests + run: brew install qemu bash coreutils w3m - name: "Adjust LIMACTL_CREATE_ARGS" run: echo "LIMACTL_CREATE_ARGS=${LIMACTL_CREATE_ARGS} --vm-type=qemu" >>$GITHUB_ENV - name: "Inject `no_timer_check` to kernel cmdline" @@ -331,7 +332,7 @@ jobs: - name: Install test dependencies run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends ovmf qemu-system-x86 qemu-utils + sudo apt-get install -y --no-install-recommends ovmf qemu-system-x86 qemu-utils w3m sudo modprobe kvm # `sudo usermod -aG kvm $(whoami)` does not take an effect on GHA sudo chown $(whoami) /dev/kvm @@ -430,7 +431,7 @@ jobs: with: template: templates/default.yaml - name: Install test dependencies - run: brew install qemu bash coreutils + run: brew install qemu bash coreutils w3m - name: Install socket_vmnet env: SOCKET_VMNET_VERSION: v1.2.0 @@ -525,7 +526,7 @@ jobs: with: template: templates/${{ matrix.template }} - name: Install test dependencies - run: brew install bash coreutils + run: brew install bash coreutils w3m - name: Uninstall qemu run: brew uninstall --ignore-dependencies --force qemu - name: Test diff --git a/hack/test-templates.sh b/hack/test-templates.sh index 0dd6f75c335..94cc70b294b 100755 --- a/hack/test-templates.sh +++ b/hack/test-templates.sh @@ -381,6 +381,14 @@ if [[ -n ${CHECKS["port-forwards"]} ]]; then limactl shell "$NAME" $sudo $CONTAINER_ENGINE rm -f nginx fi fi + if [[ ${NAME} != "alpine"* ]] && command -v w3m >/dev/null; then + INFO "Testing https://github.com/lima-vm/lima/issues/3685 ([gRPC portfwd] client connection is not closed immediately when server closed the connection)" + # Skip the test on Alpine, as systemd-run is missing + limactl shell "$NAME" systemd-run --user python3 -m http.server 3685 + # curl is not enough to reproduce https://github.com/lima-vm/lima/issues/3685 + # `w3m -dump` exits with status code 0 even on "Can't load" error. + timeout 30s bash -euxc "until w3m -dump http://localhost:3685 | grep -v \"w3m: Can't load\"; do sleep 3; done" + fi fi set +x fi From 6ca9e7f3971f8d8f6cbf8605b8eed38c5b3ec8ae Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Jul 2025 19:23:53 +0900 Subject: [PATCH 90/91] portfwdserver: use `tcpproxy` to fix `w3m -dump` issue Fix issue 3685 Signed-off-by: Akihiro Suda --- hack/test-templates.sh | 3 ++- pkg/portfwdserver/server.go | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hack/test-templates.sh b/hack/test-templates.sh index 94cc70b294b..365c5cc5522 100755 --- a/hack/test-templates.sh +++ b/hack/test-templates.sh @@ -381,9 +381,10 @@ if [[ -n ${CHECKS["port-forwards"]} ]]; then limactl shell "$NAME" $sudo $CONTAINER_ENGINE rm -f nginx fi fi - if [[ ${NAME} != "alpine"* ]] && command -v w3m >/dev/null; then + if [[ ${NAME} != "alpine"* && ${NAME} != "wsl2"* ]] && command -v w3m >/dev/null; then INFO "Testing https://github.com/lima-vm/lima/issues/3685 ([gRPC portfwd] client connection is not closed immediately when server closed the connection)" # Skip the test on Alpine, as systemd-run is missing + # Skip the test on WSL2, as port forwarding is half broken https://github.com/lima-vm/lima/pull/3686#issuecomment-3034842616 limactl shell "$NAME" systemd-run --user python3 -m http.server 3685 # curl is not enough to reproduce https://github.com/lima-vm/lima/issues/3685 # `w3m -dump` exits with status code 0 even on "Can't load" error. diff --git a/pkg/portfwdserver/server.go b/pkg/portfwdserver/server.go index 54f48a6e0af..3ded422b45a 100644 --- a/pkg/portfwdserver/server.go +++ b/pkg/portfwdserver/server.go @@ -4,11 +4,16 @@ package portfwdserver import ( + "context" "errors" "io" "net" + "os" + "strings" "time" + "github.com/containers/gvisor-tap-vsock/pkg/tcpproxy" + "github.com/lima-vm/lima/pkg/bicopy" "github.com/lima-vm/lima/pkg/guestagent/api" ) @@ -35,7 +40,23 @@ func (s *TunnelServer) Start(stream api.GuestService_TunnelServer) error { return err } rw := &GRPCServerRW{stream: stream, id: in.Id} - bicopy.Bicopy(rw, conn, nil) + + // FIXME: consolidate bicopy and tcpproxy into one + // + // The bicopy package does not seem to work with `w3m -dump`: + // https://github.com/lima-vm/lima/issues/3685 + // + // However, the tcpproxy package can't pass the CI for WSL2 (experimental): + // https://github.com/lima-vm/lima/pull/3686#issuecomment-3034842616 + if wsl2, _ := seemsWSL2(); wsl2 { + bicopy.Bicopy(rw, conn, nil) + } else { + proxy := tcpproxy.DialProxy{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + return conn, nil + }} + proxy.HandleConn(rw) + } + return nil } @@ -83,3 +104,13 @@ func (g *GRPCServerRW) SetReadDeadline(_ time.Time) error { func (g *GRPCServerRW) SetWriteDeadline(_ time.Time) error { return nil } + +// seemsWSL2 returns whether lima.env contains LIMA_CIDATA_VMTYPE=wsl2 . +// This is a temporary workaround and has to be removed. +func seemsWSL2() (bool, error) { + b, err := os.ReadFile("/mnt/lima-cidata/lima.env") + if err != nil { + return false, err + } + return strings.Contains(string(b), "LIMA_CIDATA_VMTYPE=wsl2"), nil +} From d078b84d20e0c17a1963dd38ff311d72f14d5ded Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 04:13:59 +0000 Subject: [PATCH 91/91] build(deps): bump github.com/miekg/dns from 1.1.66 to 1.1.67 --- updated-dependencies: - dependency-name: github.com/miekg/dns dependency-version: 1.1.67 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Akihiro Suda --- go.mod | 2 +- go.sum | 4 ++-- pkg/hostagent/dns/dns_test.go | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9efd8dd5b3c..30aabc5e2e1 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-shellwords v1.0.12 github.com/mdlayher/vsock v1.2.1 // gomodjail:unconfined - github.com/miekg/dns v1.1.66 // gomodjail:unconfined + github.com/miekg/dns v1.1.67 // gomodjail:unconfined github.com/mikefarah/yq/v4 v4.45.1 github.com/nxadm/tail v1.4.11 // gomodjail:unconfined github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index 2646c22034b..b16efa93b23 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnE github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= -github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= -github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= +github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= +github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/mikefarah/yq/v4 v4.45.1 h1:EW+HjKEVa55pUYFJseEHEHdQ0+ulunY+q42zF3M7ZaQ= github.com/mikefarah/yq/v4 v4.45.1/go.mod h1:djgN2vD749hpjVNGYTShr5Kmv5LYljhCG3lUTuEe3LM= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= diff --git a/pkg/hostagent/dns/dns_test.go b/pkg/hostagent/dns/dns_test.go index d21a06b5530..c32db6773c7 100644 --- a/pkg/hostagent/dns/dns_test.go +++ b/pkg/hostagent/dns/dns_test.go @@ -154,6 +154,11 @@ func (r TestResponseWriter) RemoteAddr() net.Addr { return new(TestAddr) } +// Network returns the value of the Net field of the Server (e.g., "tcp", "tcp-tls"). +func (r TestResponseWriter) Network() string { + return "" +} + // WriteMsg writes a reply back to the client. func (r TestResponseWriter) WriteMsg(newMsg *dns.Msg) error { dnsResult = newMsg