Skip to content

Commit

Permalink
feat: add config to support additional directories (runfinch#128)
Browse files Browse the repository at this point in the history
Signed-off-by: Ziwen Ning <ningziwe@amazon.com>

Issue #, if available:
runfinch#107

*Description of changes:*
add config to support additional directories according to the doc added
in runfinch#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 <ningziwe@amazon.com>
  • Loading branch information
ningziwen committed Dec 26, 2022
1 parent 1bac92a commit 0ceb060
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 11 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
48 changes: 44 additions & 4 deletions e2e/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -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)))
})
})
}
9 changes: 9 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/lima_config_applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/lima-vm/lima/pkg/limayaml"
"github.com/spf13/afero"
"github.com/xorcare/pointer"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -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 {
Expand Down
44 changes: 42 additions & 2 deletions pkg/config/lima_config_applier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 0ceb060

Please sign in to comment.