From 9a5dcaea0a1507f5f2e012a284123addd6b6133b Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Sat, 8 Jun 2024 21:51:10 +0900 Subject: [PATCH 1/5] fix: Refresh ~/.finch/confg.json based on finch.yaml Suppose we have configured the `creds_helpers` in `~/.finch/finch.yaml` as follows, and subsequently initialized a VM (`finch vm init`). ``` cpus: 6 creds_helpers: - ecr-login memory: 8GiB vmType: vz rosetta: true ``` As a result, `~/.finch/config.json` is created, and it contains the following: ``` {"credsStore":"ecr-login"} ``` This allows us to utilize the Amazon ECR Docker Credential Helper within Finch. Subsequently, suppose we stop and remove the VM (`finch vm stop` && `finch vm remove`), and then remove the `creds_helpers` configuration from `finch.yaml`. We then configure the `finch.yaml` file as follows: ``` cpus: 6 memory: 8GiB vmType: vz rosetta: true ``` As a result, when we reinitialize the VM (`finch vm init`), the expected behavior is that it will no longer use the Amazon ECR Docker Credential Helper. However, when initializing the VM, despite the absence of `creds_helpers` configuration in `finch.yaml`, the `"credsStore": "ecr-login"` remains in `config.json`, allowing the continued use of the Amazon ECR Docker Credential Helper. This behavior has been reported in the following issue: - https://github.com/runfinch/finch/issues/480 Furthermore, this issue occurs when we stop the VM (`finch vm stop`), modify `finch.yaml`, and subsequently start the VM (`finch vm start`). Consequently, we will modify the behavior to update `config.json` in accordance with the `creds_helpers` configuration in `finch.yaml` when initiating or starting the VM. Note that in this pull request, the commits are divided as follows: - Implement the logic to update `config.json` according to `finch.yaml` and change the function name (loadFinchConfig) in the config package - Modify to call the logic to update `config.json` according to `finch.yaml` on the VM side - Add unit tests for the credhelper package and modify unit tests for the config package - Modify and add Behavior-Driven Development (BDD) Tests using Ginkgo on the VM side - Add e2e tests On the other hand, in this commit, the logic to update `config.json` according to `finch.yaml` is implemented and the function name (loadFinchConfig) is changed in the config package. Signed-off-by: Hayato Kiwata --- pkg/config/config.go | 6 +-- pkg/dependency/credhelper/cred_helper.go | 68 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 714181f64..e857ab04d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -177,8 +177,8 @@ func Load( return defCfg, nil } -// loadFinchConfig Load Finch's configuration from a YAML file. -func loadFinchConfig(fs afero.Fs, finchConfigPath string, logger flog.Logger, systemDeps LoadSystemDeps, mem fmemory.Memory) (*Finch, error) { +// LoadFinchConfig Load Finch's configuration from a YAML file. +func LoadFinchConfig(fs afero.Fs, finchConfigPath string, logger flog.Logger, systemDeps LoadSystemDeps, mem fmemory.Memory) (*Finch, error) { b, err := afero.ReadFile(fs, finchConfigPath) if err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) @@ -202,7 +202,7 @@ func ModifyFinchConfig(fs afero.Fs, logger flog.Logger, finchConfigPath string, systemDeps := system.NewStdLib() mem := fmemory.NewMemory() - finchCfg, err := loadFinchConfig(fs, finchConfigPath, logger, systemDeps, mem) + finchCfg, err := LoadFinchConfig(fs, finchConfigPath, logger, systemDeps, mem) if err != nil { return isConfigUpdated, err } diff --git a/pkg/dependency/credhelper/cred_helper.go b/pkg/dependency/credhelper/cred_helper.go index cdb885adc..0c53e0835 100644 --- a/pkg/dependency/credhelper/cred_helper.go +++ b/pkg/dependency/credhelper/cred_helper.go @@ -5,16 +5,23 @@ package credhelper import ( + "encoding/json" "fmt" + "os" "path/filepath" + "slices" + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/types" "github.com/spf13/afero" "github.com/runfinch/finch/pkg/command" "github.com/runfinch/finch/pkg/config" "github.com/runfinch/finch/pkg/dependency" "github.com/runfinch/finch/pkg/flog" + "github.com/runfinch/finch/pkg/fmemory" "github.com/runfinch/finch/pkg/path" + "github.com/runfinch/finch/pkg/system" ) const ( @@ -96,3 +103,64 @@ func newDeps( return deps } + +// RefreshConfigFile refreshes config.json according to finch.yaml. +func RefreshConfigFile(fs afero.Fs, logger flog.Logger, finchConfigPath, configJSONPath string) error { + systemDeps := system.NewStdLib() + mem := fmemory.NewMemory() + + finchCfg, err := config.LoadFinchConfig(fs, finchConfigPath, logger, systemDeps, mem) + if err != nil { + return err + } + if slices.Contains(finchCfg.CredsHelpers, "ecr-login") { + return nil + } + + fileExists, err := afero.Exists(fs, configJSONPath) + if err != nil { + return err + } + if !fileExists { + return nil + } + + fileRead, err := fs.Open(configJSONPath) + if err != nil { + return err + } + defer fileRead.Close() //nolint:errcheck // closing the file + + var cfg configfile.ConfigFile + + bytes, _ := afero.ReadAll(fileRead) + err = json.Unmarshal(bytes, &cfg) + if err != nil { + return err + } + if cfg.CredentialsStore == "" { + return nil + } + + cfg.CredentialsStore = "" + if cfg.AuthConfigs == nil { + cfg.AuthConfigs = map[string]types.AuthConfig{} + } + + finalCfgBytes, err := json.Marshal(&cfg) + if err != nil { + return err + } + + file, err := fs.OpenFile(configJSONPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) + if err != nil { + return err + } + _, err = file.Write(finalCfgBytes) + if err != nil { + return err + } + defer file.Close() //nolint:errcheck // closing the file + + return nil +} From 71e165df25013b564cbda08aaed5e316f54e927b Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Sat, 8 Jun 2024 21:53:56 +0900 Subject: [PATCH 2/5] fix: Refresh ~/.finch/confg.json based on finch.yaml This commit fixes the logic to call the VM side to update config.json according to ~/.finch/finch.yaml. Signed-off-by: Hayato Kiwata --- cmd/finch/virtual_machine.go | 12 ++++++++---- cmd/finch/virtual_machine_init.go | 18 +++++++++++++++++- cmd/finch/virtual_machine_start.go | 19 ++++++++++++++++++- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/cmd/finch/virtual_machine.go b/cmd/finch/virtual_machine.go index f35769391..2d9ba2602 100644 --- a/cmd/finch/virtual_machine.go +++ b/cmd/finch/virtual_machine.go @@ -36,6 +36,7 @@ func newVirtualMachineCommand( fp path.Finch, fs afero.Fs, diskManager disk.UserDataDiskManager, + finchDir string, ) *cobra.Command { virtualMachineCommand := &cobra.Command{ Use: virtualMachineRootCmd, @@ -43,12 +44,12 @@ func newVirtualMachineCommand( } virtualMachineCommand.AddCommand( - newStartVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fs, fp.LimaSSHPrivateKeyPath(), diskManager), + newStartVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fs, fp.LimaSSHPrivateKeyPath(), diskManager, finchDir), newStopVMCommand(limaCmdCreator, diskManager, logger), newRemoveVMCommand(limaCmdCreator, diskManager, logger), newStatusVMCommand(limaCmdCreator, logger, os.Stdout), newInitVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fp.BaseYamlFilePath(), fs, - fp.LimaSSHPrivateKeyPath(), diskManager), + fp.LimaSSHPrivateKeyPath(), diskManager, finchDir), newSettingsVMCommand(logger, lca, fs, os.Stdout), ) @@ -106,10 +107,12 @@ func virtualMachineCommands( home string, finchRootPath string, ) *cobra.Command { + finchDir := fp.FinchDir(finchRootPath) + return newVirtualMachineCommand( lcc, logger, - dependencies(ecc, fc, fp, fs, lcc, logger, fp.FinchDir(finchRootPath)), + dependencies(ecc, fc, fp, fs, lcc, logger, finchDir), config.NewLimaApplier( fc, ecc, @@ -123,7 +126,7 @@ func virtualMachineCommands( fssh.NewDialer(), fs, fp.LimaSSHPrivateKeyPath(), - fp.FinchDir(finchRootPath), + finchDir, home, fp.LimaInstancePath(), fc, @@ -131,5 +134,6 @@ func virtualMachineCommands( fp, fs, disk.NewUserDataDiskManager(lcc, ecc, &afero.OsFs{}, fp, finchRootPath, fc, logger), + finchDir, ) } diff --git a/cmd/finch/virtual_machine_init.go b/cmd/finch/virtual_machine_init.go index 1b64f2309..89fdfa00c 100644 --- a/cmd/finch/virtual_machine_init.go +++ b/cmd/finch/virtual_machine_init.go @@ -5,7 +5,9 @@ package main import ( "fmt" + "path/filepath" + "github.com/runfinch/finch/pkg/dependency/credhelper" "github.com/runfinch/finch/pkg/disk" "github.com/runfinch/finch/pkg/command" @@ -28,11 +30,12 @@ func newInitVMCommand( fs afero.Fs, privateKeyPath string, diskManager disk.UserDataDiskManager, + finchDir string, ) *cobra.Command { initVMCommand := &cobra.Command{ Use: "init", Short: "Initialize the virtual machine", - RunE: newInitVMAction(lcc, logger, optionalDepGroups, lca, baseYamlFilePath, diskManager).runAdapter, + RunE: newInitVMAction(lcc, logger, optionalDepGroups, lca, baseYamlFilePath, fs, diskManager, finchDir).runAdapter, PostRunE: newPostVMStartInitAction(logger, lcc, fs, privateKeyPath, nca).runAdapter, } @@ -45,7 +48,9 @@ type initVMAction struct { logger flog.Logger optionalDepGroups []*dependency.Group limaConfigApplier config.LimaConfigApplier + fs afero.Fs diskManager disk.UserDataDiskManager + finchDir string } func newInitVMAction( @@ -54,7 +59,9 @@ func newInitVMAction( optionalDepGroups []*dependency.Group, lca config.LimaConfigApplier, baseYamlFilePath string, + fs afero.Fs, diskManager disk.UserDataDiskManager, + finchDir string, ) *initVMAction { return &initVMAction{ creator: creator, @@ -62,7 +69,9 @@ func newInitVMAction( optionalDepGroups: optionalDepGroups, limaConfigApplier: lca, baseYamlFilePath: baseYamlFilePath, + fs: fs, diskManager: diskManager, + finchDir: finchDir, } } @@ -86,6 +95,13 @@ func (iva *initVMAction) run() error { return err } + finchConfigPath := filepath.Join(iva.finchDir, "finch.yaml") + configJSONPath := filepath.Join(iva.finchDir, "config.json") + err = credhelper.RefreshConfigFile(iva.fs, iva.logger, finchConfigPath, configJSONPath) + if err != nil { + return err + } + err = dependency.InstallOptionalDeps(iva.optionalDepGroups, iva.logger) if err != nil { iva.logger.Errorf("Dependency error: %v", err) diff --git a/cmd/finch/virtual_machine_start.go b/cmd/finch/virtual_machine_start.go index a1a9d6b11..27b31aefa 100644 --- a/cmd/finch/virtual_machine_start.go +++ b/cmd/finch/virtual_machine_start.go @@ -5,7 +5,9 @@ package main import ( "fmt" + "path/filepath" + "github.com/runfinch/finch/pkg/dependency/credhelper" "github.com/runfinch/finch/pkg/disk" "github.com/runfinch/finch/pkg/command" @@ -27,11 +29,12 @@ func newStartVMCommand( fs afero.Fs, privateKeyPath string, dm disk.UserDataDiskManager, + finchDir string, ) *cobra.Command { return &cobra.Command{ Use: "start", Short: "Start the virtual machine", - RunE: newStartVMAction(lcc, logger, optionalDepGroups, lca, dm).runAdapter, + RunE: newStartVMAction(lcc, logger, optionalDepGroups, lca, fs, dm, finchDir).runAdapter, PostRunE: newPostVMStartInitAction(logger, lcc, fs, privateKeyPath, nca).runAdapter, } } @@ -41,7 +44,9 @@ type startVMAction struct { logger flog.Logger optionalDepGroups []*dependency.Group limaConfigApplier config.LimaConfigApplier + fs afero.Fs userDataDiskManager disk.UserDataDiskManager + finchDir string } func newStartVMAction( @@ -49,14 +54,18 @@ func newStartVMAction( logger flog.Logger, optionalDepGroups []*dependency.Group, lca config.LimaConfigApplier, + fs afero.Fs, dm disk.UserDataDiskManager, + finchDir string, ) *startVMAction { return &startVMAction{ creator: creator, logger: logger, optionalDepGroups: optionalDepGroups, limaConfigApplier: lca, + fs: fs, userDataDiskManager: dm, + finchDir: finchDir, } } @@ -69,6 +78,14 @@ func (sva *startVMAction) run() error { if err != nil { return err } + + finchConfigPath := filepath.Join(sva.finchDir, "finch.yaml") + configJSONPath := filepath.Join(sva.finchDir, "config.json") + err = credhelper.RefreshConfigFile(sva.fs, sva.logger, finchConfigPath, configJSONPath) + if err != nil { + return err + } + err = dependency.InstallOptionalDeps(sva.optionalDepGroups, sva.logger) if err != nil { sva.logger.Errorf("Dependency error: %v", err) From e4437b393c006fa22489e0740ef0aa6175ff96d8 Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Sat, 8 Jun 2024 21:55:19 +0900 Subject: [PATCH 3/5] test: Add and modify unit tests for credhelper package and config package In this commit, unit tests for credhelper package are added and unit tests for config package are modified. Signed-off-by: Hayato Kiwata --- pkg/config/config_test.go | 4 +- pkg/dependency/credhelper/cred_helper_test.go | 158 ++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index bf8cd0713..3d53113b6 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -597,7 +597,7 @@ func Test_ModifyFinchConfig(t *testing.T) { } } -func Test_loadFinchConfig(t *testing.T) { +func Test_LoadFinchConfig(t *testing.T) { t.Parallel() testCases := []struct { @@ -725,7 +725,7 @@ func Test_loadFinchConfig(t *testing.T) { tc.mockSvc(fs, l, deps, mem) - finchCfg, err := loadFinchConfig(fs, tc.path, l, deps, mem) + finchCfg, err := LoadFinchConfig(fs, tc.path, l, deps, mem) errMsg := "" if err != nil { errMsg = err.Error() diff --git a/pkg/dependency/credhelper/cred_helper_test.go b/pkg/dependency/credhelper/cred_helper_test.go index 0c28c74c1..006e8492e 100644 --- a/pkg/dependency/credhelper/cred_helper_test.go +++ b/pkg/dependency/credhelper/cred_helper_test.go @@ -4,12 +4,17 @@ package credhelper import ( + "path/filepath" + "runtime" "testing" + "github.com/golang/mock/gomock" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/runfinch/finch/pkg/dependency" + "github.com/runfinch/finch/pkg/mocks" ) func Test_NewDependencyGroup(t *testing.T) { @@ -27,3 +32,156 @@ func Test_newDeps(t *testing.T) { require.Equal(t, 1, len(got)) assert.IsType(t, nil, got[0]) } + +func Test_RefreshConfigFile(t *testing.T) { + t.Parallel() + + type testCaseType struct { + name string + finchDir string + mockSvc func(fs afero.Fs, finchDir string) + postRunCheck func(fs afero.Fs, configJSONPath string) (string, error) + errorExists bool + wantConfigJSON string + } + + testCases := []testCaseType{ + { + name: "should return no error if creds_helpers is configured in finch.yaml", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6GiB\ncreds_helpers:\n - ecr-login" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + }, + postRunCheck: func(_ afero.Fs, _ string) (string, error) { + return "", nil + }, + errorExists: false, + wantConfigJSON: "", + finchDir: "/.finch", + }, + { + name: "should return no error if creds_helpers is not configured in finch.yaml and config.json does not exist", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + }, + postRunCheck: func(_ afero.Fs, _ string) (string, error) { + return "", nil + }, + errorExists: false, + wantConfigJSON: "", + finchDir: "/.finch", + }, + { + name: "should return an error if config.json already exists but is invalid", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + data = `{"credsStore":}` + configJSONPath := filepath.Join(finchDir, "config.json") + require.NoError(t, afero.WriteFile(fs, configJSONPath, []byte(data), 0o600)) + }, + postRunCheck: func(_ afero.Fs, _ string) (string, error) { + return "", nil + }, + errorExists: true, + wantConfigJSON: "", + finchDir: "/.finch", + }, + { + name: "should return no error if config.json is not configured with credsStore", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + data = `{}` + configJSONPath := filepath.Join(finchDir, "config.json") + require.NoError(t, afero.WriteFile(fs, configJSONPath, []byte(data), 0o600)) + }, + postRunCheck: func(_ afero.Fs, _ string) (string, error) { + return "", nil + }, + errorExists: false, + wantConfigJSON: "", + finchDir: "/.finch", + }, + { + name: "should refresh config.json if creds_helpers is not configured in finch.yaml, but credsStore is configured in config.json", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + data = `{"credsStore":"ecr-login"}` + configJSONPath := filepath.Join(finchDir, "config.json") + require.NoError(t, afero.WriteFile(fs, configJSONPath, []byte(data), 0o600)) + }, + postRunCheck: func(fs afero.Fs, configJSONPath string) (string, error) { + fileRead, _ := fs.Open(configJSONPath) + defer fileRead.Close() //nolint:errcheck // closing the file + + bytes, err := afero.ReadAll(fileRead) + if err != nil { + return "", err + } + + return string(bytes), nil + }, + errorExists: false, + wantConfigJSON: `{"auths":{}}`, + finchDir: "/.finch", + }, + } + + darwinTestCases := []testCaseType{ + { + name: "should return an error if finch.yaml is invalid", + mockSvc: func(fs afero.Fs, finchDir string) { + data := "cpus: 2\nmemory: 6Gi" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + }, + postRunCheck: func(_ afero.Fs, _ string) (string, error) { + return "", nil + }, + errorExists: true, + wantConfigJSON: "", + finchDir: "/.finch", + }, + } + + switch runtime.GOOS { + case "windows": + break + case "darwin": + testCases = append(testCases, darwinTestCases...) + default: + t.Skip("Not running tests for " + runtime.GOOS) + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctrl := gomock.NewController(t) + fs := afero.NewMemMapFs() + l := mocks.NewLogger(ctrl) + + tc.mockSvc(fs, tc.finchDir) + + finchConfigPath := filepath.Join(tc.finchDir, "finch.yaml") + configJSONPath := filepath.Join(tc.finchDir, "config.json") + err := RefreshConfigFile(fs, l, finchConfigPath, configJSONPath) + require.Equal(t, tc.errorExists, err != nil) + + configJSON, _ := tc.postRunCheck(fs, configJSONPath) + require.Equal(t, tc.wantConfigJSON, configJSON) + }) + } +} From 4133789437f4c34588b17097c306ca37b6a33453 Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Sat, 8 Jun 2024 21:56:52 +0900 Subject: [PATCH 4/5] test: Modify and add BDD tests In this commit, Behavior-Driven Development (BDD) tests using Ginkgo are modified and added. Signed-off-by: Hayato Kiwata --- cmd/finch/virtual_machine_init_test.go | 137 ++++++++++++++++++++++-- cmd/finch/virtual_machine_start_test.go | 133 ++++++++++++++++++++++- cmd/finch/virtual_machine_test.go | 2 +- 3 files changed, 257 insertions(+), 15 deletions(-) diff --git a/cmd/finch/virtual_machine_init_test.go b/cmd/finch/virtual_machine_init_test.go index db45ab44d..80573117f 100644 --- a/cmd/finch/virtual_machine_init_test.go +++ b/cmd/finch/virtual_machine_init_test.go @@ -6,15 +6,18 @@ package main import ( "errors" "fmt" + "path/filepath" "testing" - "github.com/runfinch/finch/pkg/dependency" - "github.com/runfinch/finch/pkg/flog" - "github.com/runfinch/finch/pkg/mocks" - "github.com/golang/mock/gomock" + "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/runfinch/finch/pkg/dependency" + "github.com/runfinch/finch/pkg/flog" + "github.com/runfinch/finch/pkg/mocks" ) const mockBaseYamlFilePath = "/os/os.yaml" @@ -22,7 +25,7 @@ const mockBaseYamlFilePath = "/os/os.yaml" func TestNewInitVMCommand(t *testing.T) { t.Parallel() - cmd := newInitVMCommand(nil, nil, nil, nil, nil, "", nil, "", nil) + cmd := newInitVMCommand(nil, nil, nil, nil, nil, "", nil, "", nil, "") assert.Equal(t, cmd.Name(), "init") } @@ -38,9 +41,12 @@ func TestInitVMAction_runAdapter(t *testing.T) { *mocks.LimaCmdCreator, *mocks.Logger, *mocks.LimaConfigApplier, + afero.Fs, *mocks.UserDataDiskManager, *gomock.Controller, + string, ) + finchDir string }{ { name: "should init instance with correct BaseYamlFilePath", @@ -63,9 +69,15 @@ func TestInitVMAction_runAdapter(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) @@ -83,6 +95,7 @@ func TestInitVMAction_runAdapter(t *testing.T) { logger.EXPECT().Info("Initializing and starting Finch virtual machine...") logger.EXPECT().Info("Finch virtual machine started successfully") }, + finchDir: "/.finch", }, } @@ -95,12 +108,16 @@ func TestInitVMAction_runAdapter(t *testing.T) { logger := mocks.NewLogger(ctrl) lcc := mocks.NewLimaCmdCreator(ctrl) lca := mocks.NewLimaConfigApplier(ctrl) + fs := afero.NewMemMapFs() dm := mocks.NewUserDataDiskManager(ctrl) groups := tc.groups(ctrl) - tc.mockSvc(lcc, logger, lca, dm, ctrl) + tc.mockSvc(lcc, logger, lca, fs, dm, ctrl, tc.finchDir) - assert.NoError(t, newInitVMAction(lcc, logger, groups, lca, mockBaseYamlFilePath, dm).runAdapter(tc.command, tc.args)) + assert.NoError( + t, + newInitVMAction(lcc, logger, groups, lca, mockBaseYamlFilePath, fs, dm, tc.finchDir).runAdapter(tc.command, tc.args), + ) }) } } @@ -116,9 +133,12 @@ func TestInitVMAction_run(t *testing.T) { *mocks.LimaCmdCreator, *mocks.Logger, *mocks.LimaConfigApplier, + afero.Fs, *mocks.UserDataDiskManager, *gomock.Controller, + string, ) + finchDir string }{ { name: "should init instance with correct BaseYamlFilePath", @@ -130,9 +150,15 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) @@ -151,6 +177,7 @@ func TestInitVMAction_run(t *testing.T) { logger.EXPECT().Info("Initializing and starting Finch virtual machine...") logger.EXPECT().Info("Finch virtual machine started successfully") }, + finchDir: "/.finch", }, { name: "running VM", @@ -162,14 +189,21 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Running") }, + finchDir: "/.finch", }, { name: "stopped VM", @@ -183,14 +217,21 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") }, + finchDir: "/.finch", }, { name: "unknown VM status", @@ -202,14 +243,21 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Broken"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Broken") }, + finchDir: "/.finch", }, { name: "status command returns an error", @@ -221,13 +269,62 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, _ *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Broken"), errors.New("get status error")) }, + finchDir: "/.finch", + }, + { + name: "should refresh config.json based on finch.yaml and init VM", + wantErr: nil, + groups: func(_ *gomock.Controller) []*dependency.Group { + return nil + }, + mockSvc: func( + lcc *mocks.LimaCmdCreator, + logger *mocks.Logger, + lca *mocks.LimaConfigApplier, + fs afero.Fs, + dm *mocks.UserDataDiskManager, + ctrl *gomock.Controller, + finchDir string, + ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + data = `{"credsStore":"ecr-login"}` + configJSONPath := filepath.Join(finchDir, "config.json") + require.NoError(t, afero.WriteFile(fs, configJSONPath, []byte(data), 0o600)) + + getVMStatusC := mocks.NewCommand(ctrl) + lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) + getVMStatusC.EXPECT().Output().Return([]byte(""), nil) + logger.EXPECT().Debugf("Status of virtual machine: %s", "") + + lca.EXPECT().ConfigureDefaultLimaYaml().Return(nil) + lca.EXPECT().ConfigureOverrideLimaYaml().Return(nil) + dm.EXPECT().DetachUserDataDisk().Return(nil) + dm.EXPECT().EnsureUserDataDisk().Return(nil) + + command := mocks.NewCommand(ctrl) + lcc.EXPECT().CreateWithoutStdio("start").Return(command) + command.EXPECT().CombinedOutput() + + logger.EXPECT().Info("Initializing and starting Finch virtual machine...") + logger.EXPECT().Info("Finch virtual machine started successfully") + }, + finchDir: "/.finch", }, { // should succeed even if some optional dependencies fail to be installed @@ -249,9 +346,15 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) @@ -276,6 +379,7 @@ func TestInitVMAction_run(t *testing.T) { logger.EXPECT().Info("Initializing and starting Finch virtual machine...") logger.EXPECT().Info("Finch virtual machine started successfully") }, + finchDir: "/.finch", }, { // should succeed even if some optional dependencies fail to be installed @@ -289,9 +393,15 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) @@ -299,6 +409,7 @@ func TestInitVMAction_run(t *testing.T) { lca.EXPECT().ConfigureDefaultLimaYaml().Return(errors.New("load config fails")) }, + finchDir: "/.finch", }, { @@ -311,9 +422,15 @@ func TestInitVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) @@ -336,6 +453,7 @@ func TestInitVMAction_run(t *testing.T) { logger.EXPECT().SetFormatter(flog.Text) dm.EXPECT().DetachUserDataDisk().Return(nil) }, + finchDir: "/.finch", }, } @@ -348,12 +466,13 @@ func TestInitVMAction_run(t *testing.T) { logger := mocks.NewLogger(ctrl) lcc := mocks.NewLimaCmdCreator(ctrl) lca := mocks.NewLimaConfigApplier(ctrl) + fs := afero.NewMemMapFs() dm := mocks.NewUserDataDiskManager(ctrl) groups := tc.groups(ctrl) - tc.mockSvc(lcc, logger, lca, dm, ctrl) + tc.mockSvc(lcc, logger, lca, fs, dm, ctrl, tc.finchDir) - err := newInitVMAction(lcc, logger, groups, lca, mockBaseYamlFilePath, dm).run() + err := newInitVMAction(lcc, logger, groups, lca, mockBaseYamlFilePath, fs, dm, tc.finchDir).run() assert.Equal(t, err, tc.wantErr) }) } diff --git a/cmd/finch/virtual_machine_start_test.go b/cmd/finch/virtual_machine_start_test.go index f7e0ce5c3..1259b4977 100644 --- a/cmd/finch/virtual_machine_start_test.go +++ b/cmd/finch/virtual_machine_start_test.go @@ -6,6 +6,7 @@ package main import ( "errors" "fmt" + "path/filepath" "testing" "github.com/runfinch/finch/pkg/dependency" @@ -13,14 +14,16 @@ import ( "github.com/runfinch/finch/pkg/mocks" "github.com/golang/mock/gomock" + "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewStartVMCommand(t *testing.T) { t.Parallel() - cmd := newStartVMCommand(nil, nil, nil, nil, nil, nil, "", nil) + cmd := newStartVMCommand(nil, nil, nil, nil, nil, nil, "", nil, "") assert.Equal(t, cmd.Name(), "start") } @@ -37,9 +40,12 @@ func TestStartVMAction_runAdapter(t *testing.T) { *mocks.LimaCmdCreator, *mocks.Logger, *mocks.LimaConfigApplier, + afero.Fs, *mocks.UserDataDiskManager, *gomock.Controller, + string, ) + finchDir string }{ { name: "should start instance", @@ -63,9 +69,15 @@ func TestStartVMAction_runAdapter(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) @@ -82,6 +94,7 @@ func TestStartVMAction_runAdapter(t *testing.T) { logger.EXPECT().Info("Starting existing Finch virtual machine...") logger.EXPECT().Info("Finch virtual machine started successfully") }, + finchDir: "/.finch", }, } @@ -94,12 +107,13 @@ func TestStartVMAction_runAdapter(t *testing.T) { logger := mocks.NewLogger(ctrl) lcc := mocks.NewLimaCmdCreator(ctrl) lca := mocks.NewLimaConfigApplier(ctrl) + fs := afero.NewMemMapFs() dm := mocks.NewUserDataDiskManager(ctrl) groups := tc.groups(ctrl) - tc.mockSvc(lcc, logger, lca, dm, ctrl) + tc.mockSvc(lcc, logger, lca, fs, dm, ctrl, tc.finchDir) - err := newStartVMAction(lcc, logger, groups, lca, dm).runAdapter(tc.command, tc.args) + err := newStartVMAction(lcc, logger, groups, lca, fs, dm, tc.finchDir).runAdapter(tc.command, tc.args) assert.Equal(t, tc.wantErr, err) }) } @@ -116,9 +130,12 @@ func TestStartVMAction_run(t *testing.T) { *mocks.LimaCmdCreator, *mocks.Logger, *mocks.LimaConfigApplier, + afero.Fs, *mocks.UserDataDiskManager, *gomock.Controller, + string, ) + finchDir string }{ { name: "should start instance if instance exists", @@ -138,9 +155,64 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, + dm *mocks.UserDataDiskManager, + ctrl *gomock.Controller, + finchDir string, + ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + getVMStatusC := mocks.NewCommand(ctrl) + lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) + getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) + logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped") + + lca.EXPECT().ConfigureOverrideLimaYaml().Return(nil) + + dm.EXPECT().EnsureUserDataDisk().Return(nil) + + command := mocks.NewCommand(ctrl) + command.EXPECT().CombinedOutput() + lcc.EXPECT().CreateWithoutStdio("start", limaInstanceName).Return(command) + + logger.EXPECT().Info("Starting existing Finch virtual machine...") + logger.EXPECT().Info("Finch virtual machine started successfully") + }, + finchDir: "/.finch", + }, + { + name: "should refresh config.json based on finch.yaml and start VM", + wantErr: nil, + groups: func(ctrl *gomock.Controller) []*dependency.Group { + dep := mocks.NewDependency(ctrl) + deps := dependency.NewGroup([]dependency.Dependency{dep}, "", "") + groups := []*dependency.Group{deps} + + dep.EXPECT().Installed().Return(false) + dep.EXPECT().RequiresRoot().Return(false) + dep.EXPECT().Install().Return(nil) + + return groups + }, + mockSvc: func( + lcc *mocks.LimaCmdCreator, + logger *mocks.Logger, + lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + + data = `{"credsStore":"ecr-login"}` + configJSONPath := filepath.Join(finchDir, "config.json") + require.NoError(t, afero.WriteFile(fs, configJSONPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) @@ -157,6 +229,7 @@ func TestStartVMAction_run(t *testing.T) { logger.EXPECT().Info("Starting existing Finch virtual machine...") logger.EXPECT().Info("Finch virtual machine started successfully") }, + finchDir: "/.finch", }, { name: "running VM", @@ -168,14 +241,21 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Running") }, + finchDir: "/.finch", }, { name: "nonexistent VM", @@ -188,14 +268,21 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte(""), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "") }, + finchDir: "/.finch", }, { name: "unknown VM status", @@ -207,14 +294,21 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Broken"), nil) logger.EXPECT().Debugf("Status of virtual machine: %s", "Broken") }, + finchDir: "/.finch", }, { name: "status command returns an error", @@ -226,13 +320,20 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, _ *mocks.Logger, _ *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Broken"), errors.New("get status error")) }, + finchDir: "/.finch", }, { // TODO: split this test case up: @@ -247,9 +348,15 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, _ *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) @@ -257,6 +364,7 @@ func TestStartVMAction_run(t *testing.T) { lca.EXPECT().ConfigureOverrideLimaYaml().Return(errors.New("load config fails")) }, + finchDir: "/.finch", }, { // should succeed even if some optional dependencies fail to be installed @@ -278,9 +386,15 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) @@ -303,6 +417,7 @@ func TestStartVMAction_run(t *testing.T) { ), ) }, + finchDir: "/.finch", }, { name: "should print out error if instance fails to start", @@ -320,9 +435,15 @@ func TestStartVMAction_run(t *testing.T) { lcc *mocks.LimaCmdCreator, logger *mocks.Logger, lca *mocks.LimaConfigApplier, + fs afero.Fs, dm *mocks.UserDataDiskManager, ctrl *gomock.Controller, + finchDir string, ) { + data := "cpus: 2\nmemory: 6GiB" + finchConfigPath := filepath.Join(finchDir, "finch.yaml") + require.NoError(t, afero.WriteFile(fs, finchConfigPath, []byte(data), 0o600)) + getVMStatusC := mocks.NewCommand(ctrl) lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC) getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil) @@ -342,6 +463,7 @@ func TestStartVMAction_run(t *testing.T) { logger.EXPECT().Errorf("Finch virtual machine failed to start, debug logs:\n%s", logs) logger.EXPECT().SetFormatter(flog.Text) }, + finchDir: "/.finch", }, } @@ -354,12 +476,13 @@ func TestStartVMAction_run(t *testing.T) { logger := mocks.NewLogger(ctrl) lcc := mocks.NewLimaCmdCreator(ctrl) lca := mocks.NewLimaConfigApplier(ctrl) + fs := afero.NewMemMapFs() dm := mocks.NewUserDataDiskManager(ctrl) groups := tc.groups(ctrl) - tc.mockSvc(lcc, logger, lca, dm, ctrl) + tc.mockSvc(lcc, logger, lca, fs, dm, ctrl, tc.finchDir) - err := newStartVMAction(lcc, logger, groups, lca, dm).run() + err := newStartVMAction(lcc, logger, groups, lca, fs, dm, tc.finchDir).run() assert.Equal(t, err, tc.wantErr) }) } diff --git a/cmd/finch/virtual_machine_test.go b/cmd/finch/virtual_machine_test.go index f3dde7e8b..a61ea1d5e 100644 --- a/cmd/finch/virtual_machine_test.go +++ b/cmd/finch/virtual_machine_test.go @@ -17,7 +17,7 @@ import ( func TestVirtualMachineCommand(t *testing.T) { t.Parallel() - cmd := newVirtualMachineCommand(nil, nil, nil, nil, nil, "", nil, nil) + cmd := newVirtualMachineCommand(nil, nil, nil, nil, nil, "", nil, nil, "") assert.Equal(t, cmd.Use, virtualMachineRootCmd) // check the number of subcommand for vm From c231fd27f68f3e00f097ac322eacdea5bb9be436 Mon Sep 17 00:00:00 2001 From: Hayato Kiwata Date: Sat, 8 Jun 2024 21:57:30 +0900 Subject: [PATCH 5/5] test: Add e2e tests for the implementation In this commit, e2e tests for the implementation are added. Signed-off-by: Hayato Kiwata --- e2e/vm/config_darwin_test.go | 5 ++++- e2e/vm/config_windows_test.go | 5 ++++- e2e/vm/finch_config_file_test.go | 21 ++++++++++++++++++++- e2e/vm/vm_darwin_test.go | 2 +- e2e/vm/vm_windows_test.go | 2 +- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/e2e/vm/config_darwin_test.go b/e2e/vm/config_darwin_test.go index c9e1bebb5..477cf60bb 100644 --- a/e2e/vm/config_darwin_test.go +++ b/e2e/vm/config_darwin_test.go @@ -22,7 +22,10 @@ import ( "github.com/runfinch/finch/pkg/config" ) -var finchConfigFilePath = os.Getenv("HOME") + "/.finch/finch.yaml" +var ( + finchConfigFilePath = os.Getenv("HOME") + "/.finch/finch.yaml" + finchConfigJSONPath = os.Getenv("HOME") + "/.finch/config.json" +) func limaDataDirPath(installed bool) string { limaConfigFilePath := defaultLimaDataDirPath diff --git a/e2e/vm/config_windows_test.go b/e2e/vm/config_windows_test.go index fd25a2f54..05566cc6c 100644 --- a/e2e/vm/config_windows_test.go +++ b/e2e/vm/config_windows_test.go @@ -9,4 +9,7 @@ import ( "path/filepath" ) -var finchConfigFilePath = filepath.Join(os.Getenv("LOCALAPPDATA"), ".finch", "finch.yaml") +var ( + finchConfigFilePath = filepath.Join(os.Getenv("LOCALAPPDATA"), ".finch", "finch.yaml") + finchConfigJSONPath = filepath.Join(os.Getenv("LOCALAPPDATA"), ".finch", "config.json") +) diff --git a/e2e/vm/finch_config_file_test.go b/e2e/vm/finch_config_file_test.go index 0137c8b8f..d73a37a5e 100644 --- a/e2e/vm/finch_config_file_test.go +++ b/e2e/vm/finch_config_file_test.go @@ -20,8 +20,18 @@ import ( // testFinchConfigFile makes sure that DOCKER_CONFIG is properly set to ~/.finch so that related information // is written to ~/.finch/config.json file. -var testFinchConfigFile = func(o *option.Option) { +var testFinchConfigFile = func(o *option.Option, installed bool) { ginkgo.Describe("finch config file", func() { + var vmType string + + ginkgo.BeforeEach(func() { + if runtime.GOOS == "windows" { + vmType = "wsl2" + } else { + vmType = "vz" + } + }) + ginkgo.It("should store login credentials", func() { filename := "htpasswd" registryImage := "public.ecr.aws/docker/library/registry:2" @@ -74,5 +84,14 @@ var testFinchConfigFile = func(o *option.Option) { gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(string(configContent)).ShouldNot(gomega.ContainSubstring(registry)) }) + + ginkgo.It("should refresh config.json if creds_helpers is not set in finch.yaml, but config.json is configured with credsStore", func() { + resetVM(o) + resetDisks(o, installed) + writeFile(finchConfigFilePath, []byte(fmt.Sprintf("cpus: 6\nmemory: 4GiB\nvmType: %s\nrosetta: true", vmType))) + writeFile(finchConfigJSONPath, []byte(`{"credsStore":"ecr-login"}`)) + command.New(o, virtualMachineRootCmd, "init").WithoutCheckingExitCode().WithTimeoutInSeconds(160).Run() + gomega.Expect(string(readFile(finchConfigJSONPath))).Should(gomega.Equal(`{"auths":{}}`)) + }) }) } diff --git a/e2e/vm/vm_darwin_test.go b/e2e/vm/vm_darwin_test.go index 4652cd96e..402ac2a18 100644 --- a/e2e/vm/vm_darwin_test.go +++ b/e2e/vm/vm_darwin_test.go @@ -64,7 +64,7 @@ func TestVM(t *testing.T) { testVMLifecycle(o) testAdditionalDisk(o, *e2e.Installed) testConfig(o, *e2e.Installed) - testFinchConfigFile(o) + testFinchConfigFile(o, *e2e.Installed) testVersion(o) testNonDefaultOptions(o, *e2e.Installed) testSupportBundle(o) diff --git a/e2e/vm/vm_windows_test.go b/e2e/vm/vm_windows_test.go index 2098483d5..71d26a963 100644 --- a/e2e/vm/vm_windows_test.go +++ b/e2e/vm/vm_windows_test.go @@ -44,7 +44,7 @@ func TestVM(t *testing.T) { ginkgo.Describe("", func() { testVMLifecycle(o) testAdditionalDisk(o, *e2e.Installed) - testFinchConfigFile(o) + testFinchConfigFile(o, *e2e.Installed) testVersion(o) testSupportBundle(o) testCredHelper(o, *e2e.Installed, *e2e.Registry)