diff --git a/internal/cmd/config.go b/internal/cmd/config.go index d6be0baa381..64a6c18795d 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -124,10 +124,6 @@ func getConfig(flags *pflag.FlagSet) (Config, error) { }, nil } -func legacyConfigFilePath(gs *state.GlobalState) string { - return filepath.Join(gs.UserOSConfigDir, "loadimpact", "k6", "config.json") -} - // Reads the configuration file from the supplied filesystem and returns it or // an error. The only situation in which an error won't be returned is if the // user didn't explicitly specify a config file path and the default config file @@ -155,6 +151,10 @@ func readDiskConfig(gs *state.GlobalState) (Config, error) { return conf, nil } +func legacyConfigFilePath(gs *state.GlobalState) string { + return filepath.Join(gs.UserOSConfigDir, "loadimpact", "k6", "config.json") +} + func readLegacyDiskConfig(gs *state.GlobalState) (Config, error) { // Try to see if the legacy config exists in the supplied filesystem legacyPath := filepath.Join(gs.UserOSConfigDir, "loadimpact", "k6", "config.json") @@ -218,7 +218,7 @@ func getConsolidatedConfig(gs *state.GlobalState, cliConf Config, runnerOpts lib } else if err != nil { return conf, errext.WithExitCodeIfNone(err, exitcodes.InvalidConfig) } else { - gs.Logger.Warn("The configuration file has been found on the old path. Please, run again `k6 cloud login` or `k6 login` commands to migrate it to the new path. If you migrated it manually, then remove the old config file.") + gs.Logger.Warn("The configuration file has been found on the old path. Please, run again `k6 cloud login` or `k6 login` commands to migrate to the new path. If you already migrated it manually, then remove the file from the old path.\n\n") } envConf, err := readEnvConfig(gs.Env) @@ -333,3 +333,34 @@ func validateScenarioConfig(conf lib.ExecutorConfig, isExecutable func(string) b } return nil } + +// migrateLegacyConfigFileIfAny moves the configuration file from +// the old default `~/.config/loadimpact/...` folder +// to the new `~/.config/k6/...` default folder. +func migrateLegacyConfigFileIfAny(gs *state.GlobalState) error { + fn := func() error { + legacyFpath := legacyConfigFilePath(gs) + if _, err := gs.FS.Stat(legacyFpath); errors.Is(err, fs.ErrNotExist) { + return nil + } else if err != nil { + return err + } + + if err := gs.FS.MkdirAll(filepath.Dir(gs.Flags.ConfigFilePath), 0o755); err != nil { + return err + } + + err := gs.FS.Rename(legacyFpath, gs.Flags.ConfigFilePath) + if err != nil { + return err + } + + gs.Logger.Infof("Note, the configuration file has been migrated "+ + "from old default path (%q) to the new version (%q).\n\n", legacyFpath, gs.Flags.ConfigFilePath) + return nil + } + if err := fn(); err != nil { + return fmt.Errorf("move from the old to the new configuration's filepath failed: %w", err) + } + return nil +} diff --git a/internal/cmd/config_test.go b/internal/cmd/config_test.go index 9c03d320598..52953ea2507 100644 --- a/internal/cmd/config_test.go +++ b/internal/cmd/config_test.go @@ -2,11 +2,13 @@ package cmd import ( "encoding/json" + "io" "io/fs" "testing" "time" "github.com/mstoykov/envconfig" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v3" @@ -213,7 +215,7 @@ func TestReadDiskConfigWithDefaultFlags(t *testing.T) { memfs := fsext.NewMemMapFs() conf := []byte(`{"iterations":1028,"cloud":{"field1":"testvalue"}}`) - defaultConfigPath := ".config/loadimpact/k6/config.json" + defaultConfigPath := ".config/k6/config.json" require.NoError(t, fsext.WriteFile(memfs, defaultConfigPath, conf, 0o644)) defaultFlags := state.GetDefaultFlags(".config") @@ -298,7 +300,7 @@ func TestReadDiskConfigNotJSONContentError(t *testing.T) { memfs := fsext.NewMemMapFs() conf := []byte(`bad json format`) - defaultConfigPath := ".config/loadimpact/k6/config.json" + defaultConfigPath := ".config/k6/config.json" require.NoError(t, fsext.WriteFile(memfs, defaultConfigPath, conf, 0o644)) gs := &state.GlobalState{ @@ -342,7 +344,7 @@ func TestWriteDiskConfigWithDefaultFlags(t *testing.T) { err := writeDiskConfig(gs, c) require.NoError(t, err) - finfo, err := memfs.Stat(".config/loadimpact/k6/config.json") + finfo, err := memfs.Stat(".config/k6/config.json") require.NoError(t, err) assert.NotEmpty(t, finfo.Size()) } @@ -352,7 +354,7 @@ func TestWriteDiskConfigOverwrite(t *testing.T) { memfs := fsext.NewMemMapFs() conf := []byte(`{"iterations":1028,"cloud":{"field1":"testvalue"}}`) - defaultConfigPath := ".config/loadimpact/k6/config.json" + defaultConfigPath := ".config/k6/config.json" require.NoError(t, fsext.WriteFile(memfs, defaultConfigPath, conf, 0o644)) defaultFlags := state.GetDefaultFlags(".config") @@ -405,3 +407,48 @@ func TestWriteDiskConfigNoJSONContentError(t *testing.T) { var serr *json.SyntaxError assert.ErrorAs(t, err, &serr) } + +func TestMigrateLegacyConfigFileIfAny(t *testing.T) { + memfs := fsext.NewMemMapFs() + + conf := []byte(`{"iterations":1028,"cloud":{"field1":"testvalue"}}`) + legacyConfigPath := ".config/loadimpact/k6/config.json" + fsext.WriteFile(memfs, legacyConfigPath, conf, 0o644) + + logger := logrus.New() + logger.SetOutput(io.Discard) + + defaultFlags := state.GetDefaultFlags(".config") + gs := &state.GlobalState{ + FS: memfs, + Flags: defaultFlags, + DefaultFlags: defaultFlags, + UserOSConfigDir: ".config", + Logger: logger, + } + + err := migrateLegacyConfigFileIfAny(gs) + require.NoError(t, err) + + f, err := fsext.ReadFile(memfs, ".config/k6/config.json") + require.NoError(t, err) + assert.Equal(t, f, conf) + + _, err = memfs.Stat(legacyConfigPath) + assert.ErrorIs(t, err, fs.ErrNotExist) +} + +func TestMigrateLegacyConfigFileIfAnyWhenFileDoesNotExist(t *testing.T) { + memfs := fsext.NewMemMapFs() + + defaultFlags := state.GetDefaultFlags(".config") + gs := &state.GlobalState{ + FS: memfs, + Flags: defaultFlags, + DefaultFlags: defaultFlags, + UserOSConfigDir: ".config", + } + + err := migrateLegacyConfigFileIfAny(gs) + require.NoError(t, err) +}