From 0ceb0602c6e1b90b25fa9de19303ea6ccdece6d7 Mon Sep 17 00:00:00 2001 From: Ziwen Ning Date: Sun, 25 Dec 2022 21:44:12 -0800 Subject: [PATCH] feat: add config to support additional directories (#128) Signed-off-by: Ziwen Ning Issue #, if available: https://github.com/runfinch/finch/issues/107 *Description of changes:* add config to support additional directories according to the doc added in #123 *Testing done:* - [ X ] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Ziwen Ning --- README.md | 13 ++++--- e2e/config_test.go | 48 +++++++++++++++++++++++--- pkg/config/config.go | 9 +++++ pkg/config/lima_config_applier.go | 7 ++++ pkg/config/lima_config_applier_test.go | 44 +++++++++++++++++++++-- 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index aa63da6d3..2fc6b0ca4 100644 --- a/README.md +++ b/README.md @@ -94,18 +94,21 @@ The `run` command has a `-v` option for volume mounts. See `Volume flags` under Finch has a simple and extensible configuration. A configuration file at `${HOME}/.finch/finch.yaml` will be generated on first run. Currently, this config file has options for system resource limits for the underlying virtual machine. These default limits are generated dynamically based on the resources available on the host system, but can be changed by manually editing the config file. -Currently, the options are: - -* CPUs [int]: the amount of vCPU to dedicate to the virtual machine -* Memory [string]: the amount of memory to dedicate to the virtual machine - For a full list of configuration options, check [the struct here](pkg/config/config.go#L25). An example `finch.yaml` looks like this: ```yaml +# CPUs: the amount of vCPU to dedicate to the virtual machine. (required) cpus: 4 +# Memory: the amount of memory to dedicate to the virtual machine. (required) memory: 4GiB +# AdditionalDirectories: the work directories that are not supported by default. In macOS, only home directory is supported by default. +# For example, if you want to mount a directory into a container, and that directory is not under your home directory, +# then you'll need to specify this field to add that directory or any ascendant of it as a work directory. (optional) +additional_directories: + # the path of each additional directory. + - path: /Volumes ``` ## What's next? diff --git a/e2e/config_test.go b/e2e/config_test.go index 0c4a29d35..bbaad6913 100644 --- a/e2e/config_test.go +++ b/e2e/config_test.go @@ -10,11 +10,14 @@ import ( "os/exec" "path/filepath" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" "github.com/runfinch/common-tests/command" "github.com/runfinch/common-tests/option" + "github.com/xorcare/pointer" + "gopkg.in/yaml.v3" ) var finchConfigFilePath = os.Getenv("HOME") + "/.finch/finch.yaml" @@ -86,7 +89,12 @@ var testConfig = func(o *option.Option, installed bool) { gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - gomega.Expect(cfgBuf).Should(gomega.SatisfyAll(gomega.ContainSubstring("cpus: 6"), gomega.ContainSubstring("memory: 4GiB"))) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(*limaCfg.CPUs).Should(gomega.Equal(6)) + gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("4GiB")) }) ginkgo.It("updates config values when partial config file is present", func() { @@ -96,8 +104,12 @@ var testConfig = func(o *option.Option, installed bool) { gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - // 4 CPUs is the default - gomega.Expect(cfgBuf).Should(gomega.SatisfyAll(gomega.MatchRegexp(`cpus: \d`), gomega.ContainSubstring("memory: 6GiB"))) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(limaCfg.CPUs).ShouldNot(gomega.BeNil()) + gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("6GiB")) }) ginkgo.It("uses the default config values when no config file is present", func() { @@ -107,7 +119,12 @@ var testConfig = func(o *option.Option, installed bool) { gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - gomega.Expect(cfgBuf).Should(gomega.SatisfyAll(gomega.MatchRegexp(`cpus: \d`), gomega.MatchRegexp(`memory: \dGiB`))) + + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(limaCfg.CPUs).ShouldNot(gomega.BeNil()) + gomega.Expect(*limaCfg.Memory).Should(gomega.MatchRegexp(`\dGiB`)) }) ginkgo.It("fails to launch when the config file is improperly formatted", func() { @@ -124,5 +141,28 @@ var testConfig = func(o *option.Option, installed bool) { startCmdSession := updateAndApplyConfig(o, []byte("memory: 0GiB")) gomega.Expect(startCmdSession).Should(gexec.Exit(1)) }) + + ginkgo.It("updates config values when a config file is present with additional directories", func() { + startCmdSession := updateAndApplyConfig(o, []byte(`memory: 4GiB +cpus: 6 +additional_directories: + - path: /Volumes + - path: /tmp/workspace`)) + gomega.Expect(startCmdSession).Should(gexec.Exit(0)) + + gomega.Expect(limaConfigFilePath).Should(gomega.BeARegularFile()) + cfgBuf, err := os.ReadFile(filepath.Clean(limaConfigFilePath)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(cfgBuf, &limaCfg) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(*limaCfg.CPUs).Should(gomega.Equal(6)) + gomega.Expect(*limaCfg.Memory).Should(gomega.Equal("4GiB")) + gomega.Expect(len(limaCfg.Mounts)).Should(gomega.Equal(2)) + gomega.Expect(limaCfg.Mounts[0].Location).Should(gomega.Equal("/Volumes")) + gomega.Expect(limaCfg.Mounts[0].Writable).Should(gomega.Equal(pointer.Bool(true))) + gomega.Expect(limaCfg.Mounts[1].Location).Should(gomega.Equal("/tmp/workspace")) + gomega.Expect(limaCfg.Mounts[1].Writable).Should(gomega.Equal(pointer.Bool(true))) + }) }) } diff --git a/pkg/config/config.go b/pkg/config/config.go index b512323fd..2eaaac9b8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -22,10 +22,19 @@ import ( "github.com/runfinch/finch/pkg/system" ) +// AdditionalDirectory represents the additional directory used in Finch config. +type AdditionalDirectory struct { + Path *string `yaml:"path"` +} + // Finch represents the configuration file for Finch CLI. type Finch struct { CPUs *int `yaml:"cpus"` Memory *string `yaml:"memory"` + // AdditionalDirectories are the work directories that are not supported by default. In macOS, only home directory is supported by default. + // For example, if you want to mount a directory into a container, and that directory is not under your home directory, + // then you'll need to specify this field to add that directory or any ascendant of it as a work directory. + AdditionalDirectories []AdditionalDirectory `yaml:"additional_directories,omitempty"` } // Nerdctl is a copy from github.com/containerd/nerdctl/cmd/nerdctl/main.go diff --git a/pkg/config/lima_config_applier.go b/pkg/config/lima_config_applier.go index 274cc0f2b..7180ea0c7 100644 --- a/pkg/config/lima_config_applier.go +++ b/pkg/config/lima_config_applier.go @@ -8,6 +8,7 @@ import ( "github.com/lima-vm/lima/pkg/limayaml" "github.com/spf13/afero" + "github.com/xorcare/pointer" "gopkg.in/yaml.v3" ) @@ -43,6 +44,12 @@ func (lca *limaConfigApplier) Apply() error { limaCfg.CPUs = lca.cfg.CPUs limaCfg.Memory = lca.cfg.Memory + limaCfg.Mounts = []limayaml.Mount{} + for _, ad := range lca.cfg.AdditionalDirectories { + limaCfg.Mounts = append(limaCfg.Mounts, limayaml.Mount{ + Location: *ad.Path, Writable: pointer.Bool(true), + }) + } limaCfgBytes, err := yaml.Marshal(limaCfg) if err != nil { diff --git a/pkg/config/lima_config_applier_test.go b/pkg/config/lima_config_applier_test.go index d2d8a53c9..ad8940f57 100644 --- a/pkg/config/lima_config_applier_test.go +++ b/pkg/config/lima_config_applier_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/golang/mock/gomock" + "github.com/lima-vm/lima/pkg/limayaml" "github.com/spf13/afero" "github.com/stretchr/testify/require" "github.com/xorcare/pointer" @@ -44,8 +45,11 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { buf, err := afero.ReadFile(fs, "/lima.yaml") require.NoError(t, err) - // limayaml.LimaYAML has a required "images" field which will also get marshaled - require.Equal(t, buf, []byte("images: []\ncpus: 4\nmemory: 2GiB\n")) + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) }, want: nil, }, @@ -81,6 +85,42 @@ func TestDiskLimaConfigApplier_Apply(t *testing.T) { &yaml.TypeError{Errors: []string{"line 1: cannot unmarshal !!str `this is...` into limayaml.LimaYAML"}}, ), }, + { + name: "lima config file with additional directories", + config: &Finch{ + Memory: pointer.String("2GiB"), + CPUs: pointer.Int(4), + AdditionalDirectories: []AdditionalDirectory{{pointer.String("/Volumes")}}, + }, + path: "/lima.yaml", + mockSvc: func(fs afero.Fs, l *mocks.Logger) { + err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600) + require.NoError(t, err) + }, + postRunCheck: func(t *testing.T, fs afero.Fs) { + buf, err := afero.ReadFile(fs, "/lima.yaml") + require.NoError(t, err) + + // limayaml.LimaYAML has a required "images" field which will also get marshaled + wantYaml := `images: [] +cpus: 4 +memory: 2GiB +mounts: + - location: /Volumes + writable: true +` + require.Equal(t, wantYaml, string(buf)) + var limaCfg limayaml.LimaYAML + err = yaml.Unmarshal(buf, &limaCfg) + require.NoError(t, err) + require.Equal(t, 4, *limaCfg.CPUs) + require.Equal(t, "2GiB", *limaCfg.Memory) + require.Equal(t, 1, len(limaCfg.Mounts)) + require.Equal(t, "/Volumes", limaCfg.Mounts[0].Location) + require.Equal(t, true, *limaCfg.Mounts[0].Writable) + }, + want: nil, + }, } for _, tc := range testCases {