Repository: chanzuckerberg/fogg
Pull request #318 (2019-07-01 20:32:24)

[feature] New command to run all possible migrations 
### Summary
Created an interface for current and future fogg migrations. The interface allows all migrations to run in a single place. The interface requires that all migrations support Guard and Migrate methods. Consolidated all migrations (versioning and file type into a single folder)

- `fogg migrate` defaults to fogg.yml; however, a fogg.json can be provided. 
- Also improved logging information, to give more specific user info

### Test Plan
- Added yaml_test.go to test migrating from fogg.json to fogg.yml (~85% code coverage). 
- Updated upgrade_test.go to support new interface (~85% code coverage).



codecov[bot]: # [Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=h1) Report
> Merging [#318](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=desc) into [master](https://codecov.io/gh/chanzuckerberg/fogg/commit/59ae56a159369506570dd4f2e7dd2b5c0a6cb4bf?src=pr&el=desc) will **decrease** coverage by `0.7%`.
> The diff coverage is `56.75%`.

[![Impacted file tree graph](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/graphs/tree.svg?width=650&token=57DhtZ7g2A&height=150&src=pr)](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #318      +/-   ##
==========================================
- Coverage   81.31%   80.61%   -0.71%     
==========================================
  Files          18       20       +2     
  Lines        1702     1754      +52     
==========================================
+ Hits         1384     1414      +30     
- Misses        193      208      +15     
- Partials      125      132       +7
```


| [Impacted Files](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [migrations/migrations.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/diff?src=pr&el=tree#diff-bWlncmF0aW9ucy9taWdyYXRpb25zLmdv) | `0% <0%> (ø)` | |
| [migrations/yaml.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/diff?src=pr&el=tree#diff-bWlncmF0aW9ucy95YW1sLmdv) | `69.23% <69.23%> (ø)` | |
| [migrations/upgrade.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/diff?src=pr&el=tree#diff-bWlncmF0aW9ucy91cGdyYWRlLmdv) | `70.58% <70.58%> (ø)` | |
| [config/config.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/diff?src=pr&el=tree#diff-Y29uZmlnL2NvbmZpZy5nbw==) | `92.34% <0%> (-2.19%)` | :arrow_down: |
| [errs/errs.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/318/diff?src=pr&el=tree#diff-ZXJycy9lcnJzLmdv) | `63.33% <0%> (+10%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=footer). Last update [59ae56a...7bb235f](https://codecov.io/gh/chanzuckerberg/fogg/pull/318?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cmd/migrate.go b/cmd/migrate.go
new file mode 100644
index 00000000..3a7db482
--- /dev/null
+++ b/cmd/migrate.go
@@ -0,0 +1,45 @@
+package cmd
+
+import (
+	"os"
+
+	"github.com/chanzuckerberg/fogg/errs"
+	"github.com/chanzuckerberg/fogg/migrations"
+	"github.com/spf13/afero"
+	"github.com/spf13/cobra"
+)
+
+func init() {
+	migrateCommand.Flags().StringP("config", "c", "fogg.json", "Use this to override the fogg config file.")
+	migrateCommand.Flags().BoolP("force", "f", false, "Use this to skip all of the migration prompts.")
+	rootCmd.AddCommand(migrateCommand)
+}
+
+var migrateCommand = &cobra.Command{
+	Use:   "migrate",
+	Short: "Runs all possible fogg migrations",
+	Long:  "This command will run all applicable updates to fogg configuration.",
+	RunE: func(cmd *cobra.Command, args []string) error {
+
+		// Set up fs
+		pwd, err := os.Getwd()
+		if err != nil {
+			return errs.WrapUser(err, "can't get pwd")
+		}
+		fs := afero.NewBasePathFs(afero.NewOsFs(), pwd)
+
+		configFile, err := cmd.Flags().GetString("config")
+		if err != nil {
+			return errs.WrapInternal(err, "couldn't parse config flag")
+		}
+
+		skipPrompts, err := cmd.Flags().GetBool("force")
+		if err != nil {
+			return errs.WrapInternal(err, "couldn't parse skip flag")
+		}
+
+		openGitOrExit(fs)
+
+		return migrations.RunMigrations(fs, configFile, skipPrompts)
+	},
+}
diff --git a/cmd/upgrade.go b/cmd/upgrade.go
deleted file mode 100644
index a7c3a2a0..00000000
--- a/cmd/upgrade.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package cmd
-
-import (
-	"os"
-
-	"github.com/chanzuckerberg/fogg/config"
-	"github.com/chanzuckerberg/fogg/errs"
-	"github.com/spf13/afero"
-	"github.com/spf13/cobra"
-)
-
-func init() {
-	upgrade.Flags().StringP("config", "c", "fogg.yml", "Use this to override the fogg config file.")
-	rootCmd.AddCommand(upgrade)
-}
-
-var upgrade = &cobra.Command{
-	Use:   "upgrade",
-	Short: "Upgrades a fogg config",
-	Long: `This command will upgrade a fogg config.
-	Note that this might be a lossy transformation.`,
-	RunE: func(cmd *cobra.Command, args []string) error {
-		// Set up fs
-		pwd, err := os.Getwd()
-		if err != nil {
-			return errs.WrapUser(err, "can't get pwd")
-		}
-		fs := afero.NewBasePathFs(afero.NewOsFs(), pwd)
-
-		// handle flags
-		configFile, err := cmd.Flags().GetString("config")
-		if err != nil {
-			return errs.WrapInternal(err, "couldn't parse config flag")
-		}
-
-		// check that we are at root of initialized git repo
-		openGitOrExit(fs)
-		return config.Upgrade(fs, configFile)
-	},
-}
diff --git a/cmd/yaml.go b/cmd/yaml.go
deleted file mode 100644
index ad110fc2..00000000
--- a/cmd/yaml.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package cmd
-
-import (
-	"os"
-
-	"github.com/chanzuckerberg/fogg/errs"
-	"github.com/chanzuckerberg/fogg/migrate"
-	"github.com/spf13/afero"
-	"github.com/spf13/cobra"
-)
-
-func init() {
-	yamlMigrateCmd.Flags().StringP("config", "c", "fogg.json", "Use this to override the fogg config file.")
-	rootCmd.AddCommand(yamlMigrateCmd)
-}
-
-var yamlMigrateCmd = &cobra.Command{
-	Use:   "yaml-migrate",
-	Short: "Converts existing fogg.json to fogg.yml",
-	Long:  "This command will convert the fogg.json to a fogg.yml file type.",
-	RunE: func(cmd *cobra.Command, args []string) error {
-
-		// Set up fs
-		pwd, err := os.Getwd()
-		if err != nil {
-			return errs.WrapUser(err, "can't get pwd")
-		}
-		fs := afero.NewBasePathFs(afero.NewOsFs(), pwd)
-
-		configFile, err := cmd.Flags().GetString("config")
-		if err != nil {
-			return errs.WrapInternal(err, "couldn't parse config flag")
-		}
-
-		openGitOrExit(fs)
-
-		return migrate.ConvertToYaml(fs, configFile)
-	},
-}
diff --git a/config/config.go b/config/config.go
index 1956a8f0..0ff28ad9 100644
--- a/config/config.go
+++ b/config/config.go
@@ -50,7 +50,7 @@ func InitConfig(project, region, bucket, table, awsProfile, owner, awsProviderVe
 func FindConfig(fs afero.Fs, configFile string) ([]byte, int, error) {
 	f, err := fs.Open(configFile)
 	if err != nil {
-		return nil, 0, errs.WrapUser(err, "unable to open config file")
+		return nil, 0, errs.WrapUserf(err, "unable to open config file %s (Maybe specify the config file with a --config flag)", configFile)
 	}
 	defer f.Close()
 
diff --git a/config/upgrade.go b/config/upgrade.go
deleted file mode 100644
index 80b21823..00000000
--- a/config/upgrade.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package config
-
-import (
-	v1 "github.com/chanzuckerberg/fogg/config/v1"
-	"github.com/chanzuckerberg/fogg/errs"
-	"github.com/sirupsen/logrus"
-	"github.com/spf13/afero"
-	"gopkg.in/yaml.v3"
-)
-
-// Upgrade applies in-place upgrades to a configFile
-func Upgrade(fs afero.Fs, configFile string) error {
-	bytes, version, err := FindConfig(fs, configFile)
-	if err != nil {
-		return err
-	}
-	switch version {
-	case 1:
-		c1, err := v1.ReadConfig(bytes)
-		if err != nil {
-			return err
-		}
-		c2, err := UpgradeConfigVersion(c1)
-		if err != nil {
-			return err
-		}
-
-		marshalled, err := yaml.Marshal(c2)
-		if err != nil {
-			return errs.WrapInternal(err, "Could not serialize config to yaml.")
-		}
-		err = afero.WriteFile(fs, configFile, marshalled, 0644)
-		return errs.WrapInternal(err, "Could not write config to disk")
-	case 2:
-		logrus.Infof("config already v%d, nothing to do", version)
-		return nil
-
-	default:
-		return errs.NewUserf("config version %d unrecognized", version)
-	}
-}
diff --git a/migrate/yaml.go b/migrate/yaml.go
deleted file mode 100644
index ff95ed38..00000000
--- a/migrate/yaml.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package migrate
-
-import (
-	"github.com/chanzuckerberg/fogg/config"
-	"github.com/sirupsen/logrus"
-	"github.com/spf13/afero"
-	"gopkg.in/yaml.v3"
-)
-
-//ConvertToYaml method converts fogg.json to fogg.yml
-func ConvertToYaml(fs afero.Fs, configFile string) error {
-	config, err := config.FindAndReadConfig(fs, configFile)
-	if err != nil {
-		return err
-	}
-
-	yml, err := yaml.Marshal(config)
-	if err != nil {
-		return err
-	}
-
-	err = afero.WriteFile(fs, "fogg.yml", yml, 0644)
-	if err != nil {
-		return err
-	}
-	logrus.Info("Created fogg.yml config file")
-
-	err = fs.Remove(configFile)
-	if err != nil {
-		return err
-	}
-	logrus.Infof("Removed %s config file", configFile)
-
-	return nil
-}
diff --git a/migrations/migrations.go b/migrations/migrations.go
new file mode 100644
index 00000000..24ce2ab0
--- /dev/null
+++ b/migrations/migrations.go
@@ -0,0 +1,45 @@
+package migrations
+
+import (
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/afero"
+)
+
+//Migration Defines a fogg migration and the actions that it can perform
+type Migration interface {
+	Description() string                      //Describes the migration taking
+	Guard(afero.Fs, string) (bool, error)     //Returns true if migration is runnable, otherwise error
+	Migrate(afero.Fs, string) (string, error) //Returns path to config and err
+	Prompt() bool                             //Returns whether the user would like to run the migration
+}
+
+//RunMigrations cycles through a list of migrations and applies them if necessary
+func RunMigrations(fs afero.Fs, configFile string, skipPrompts bool) error {
+	migrations := []Migration{
+		&VersionUpgradeMigration{}, //This runs first because v1 to v2 uses json.Marshal
+		&JSONToYamlMigration{},
+	}
+
+	for _, migration := range migrations {
+		shouldRun, err := migration.Guard(fs, configFile)
+		if err != nil {
+			return err
+		}
+		if !shouldRun{
+			continue
+		}
+
+		//If the user does not want to run this migration
+		if skipPrompts || !migration.Prompt(){
+			logrus.Debugf("%s was skipped", migration.Description())
+			continue
+		}
+
+		configFile, err = migration.Migrate(fs, configFile)
+		if err != nil {
+			return err
+		}
+		logrus.Infof("%s was successful", migration.Description())
+	}
+	return nil
+}
diff --git a/migrations/upgrade.go b/migrations/upgrade.go
new file mode 100644
index 00000000..35ba908e
--- /dev/null
+++ b/migrations/upgrade.go
@@ -0,0 +1,77 @@
+package migrations
+
+import (
+	"github.com/chanzuckerberg/fogg/config"
+	v1 "github.com/chanzuckerberg/fogg/config/v1"
+	"github.com/chanzuckerberg/fogg/errs"
+	prompt "github.com/segmentio/go-prompt"
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/afero"
+	"gopkg.in/yaml.v3"
+)
+
+//VersionUpgradeMigration Defines a fogg version upgrade
+type VersionUpgradeMigration struct {
+}
+
+//Description Describes the upgrade taking place
+func (m *VersionUpgradeMigration) Description() string {
+	return "v1 to v2 upgrade"
+}
+
+//Guard Checks the version of the config file and determines whether an upgrade is necessary
+func (m *VersionUpgradeMigration) Guard(fs afero.Fs, configFile string) (bool, error) {
+	_, version, err := config.FindConfig(fs, configFile)
+	if err != nil {
+		return false, err
+	}
+
+	switch version {
+	case 1: //Upgrade is needed
+		return true, nil
+	case 2: //Latest version
+		return false, nil
+	default:
+		return false, errs.NewUser("Config file version was not recognized")
+	}
+}
+
+//Migrate Upgrades config file to most recent version
+func (m *VersionUpgradeMigration) Migrate(fs afero.Fs, configFile string) (string, error) {
+	bytes, version, err := config.FindConfig(fs, configFile)
+	if err != nil {
+		return "", err
+	}
+	logrus.Infof("Attempting to upgrade v%d to the latest version", version)
+
+	switch version {
+	case 1: // Upgrades to v2 and craetes yaml file
+		c1, err := v1.ReadConfig(bytes)
+		if err != nil {
+			return "", err
+		}
+		c2, err := config.UpgradeConfigVersion(c1)
+		if err != nil {
+			return "", err
+		}
+
+		marshalled, err := yaml.Marshal(c2)
+		if err != nil {
+			return "", errs.WrapInternal(err, "Could not serialize config to yaml.")
+		}
+		err = afero.WriteFile(fs, configFile, marshalled, 0644)
+		return configFile, errs.WrapInternal(err, "Could not write config to disk")
+
+	case 2: // Already v2, do nothing
+		logrus.Infof("config already v%d, nothing to do", version)
+		return configFile, nil
+
+	default:
+		return configFile, errs.NewUserf("config version %d was not recognized", version)
+	}
+}
+
+//Prompt Checks to see if the user wants their version to be upgraded
+func (m *VersionUpgradeMigration) Prompt() bool {
+	return prompt.Confirm("Would you like to upgrade from v1 to v2?")
+}
diff --git a/migrations/yaml.go b/migrations/yaml.go
new file mode 100644
index 00000000..580edafe
--- /dev/null
+++ b/migrations/yaml.go
@@ -0,0 +1,67 @@
+package migrations
+
+import (
+	"path/filepath"
+
+	"github.com/chanzuckerberg/fogg/config"
+	"github.com/chanzuckerberg/fogg/errs"
+	prompt "github.com/segmentio/go-prompt"
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/afero"
+	"gopkg.in/yaml.v3"
+)
+
+//JSONToYamlMigration Defines a fogg json to yaml config file migration
+type JSONToYamlMigration struct {
+}
+
+//Description Describes the filetype migration taking place
+func (m *JSONToYamlMigration) Description() string {
+	return ".json to .yml migration"
+}
+
+//Guard Checks to see if config file needs to be converted to .yml
+func (m *JSONToYamlMigration) Guard(fs afero.Fs, configFile string) (bool, error) {
+	ext := filepath.Ext(configFile)
+
+	switch ext {
+	case ".json":
+		return true, nil
+	case ".yml", ".yaml":
+		return false, nil
+	default:
+		return false, errs.NewUserf("Config file %s was not recognized", configFile)
+	}
+}
+
+//Migrate method converts fogg.json to fogg.yml
+func (m *JSONToYamlMigration) Migrate(fs afero.Fs, configFile string) (string, error) {
+	logrus.Infof("Migrating %s to .yml file type", configFile)
+	config, err := config.FindAndReadConfig(fs, configFile)
+	if err != nil {
+		return "", err
+	}
+
+	yml, err := yaml.Marshal(config)
+	if err != nil {
+		return "", err
+	}
+
+	err = afero.WriteFile(fs, "fogg.yml", yml, 0644)
+	if err != nil {
+		return "", err
+	}
+	logrus.Info("Created fogg.yml config file")
+
+	err = fs.Remove(configFile)
+	if err != nil {
+		return "", err
+	}
+	logrus.Infof("Removed %s config file", configFile)
+	return "fogg.yml", nil
+}
+
+//Prompt Checks to see if the user wants their config file to be migrated to yml
+func (m *JSONToYamlMigration) Prompt() bool {
+	return prompt.Confirm("Would you like to migrate your config file to .yml")
+}


Test patch:
diff --git a/config/upgrade_test.go b/config/upgrade_test.go
deleted file mode 100644
index 541c4438..00000000
--- a/config/upgrade_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package config
-
-import (
-	"testing"
-
-	"github.com/chanzuckerberg/fogg/util"
-	"github.com/spf13/afero"
-	"github.com/stretchr/testify/require"
-)
-
-func TestUpgradeV2(t *testing.T) {
-	r := require.New(t)
-	confPath := "fogg.yml"
-	conf := []byte(`{"version": 1}`)
-	fs, _, err := util.TestFs()
-	r.Nil(err)
-	err = afero.WriteFile(fs, confPath, conf, 0644)
-	r.Nil(err)
-	err = Upgrade(fs, confPath)
-	r.Nil(err)
-}
-
-func TestUpgradeUnknownVersion(t *testing.T) {
-	r := require.New(t)
-	confPath := "config"
-	conf := []byte(`{"version": 100}`)
-	fs, _, err := util.TestFs()
-	r.Nil(err)
-	err = afero.WriteFile(fs, confPath, conf, 0644)
-	r.Nil(err)
-	err = Upgrade(fs, confPath)
-	r.Error(err, "config version 100 unrecognized")
-}
-
-func TestUpgradeV1(t *testing.T) {
-	r := require.New(t)
-	confPath := "fogg.yml"
-	fs, _, err := util.TestFs()
-	r.Nil(err)
-
-	v1, err := util.TestFile("v1_full")
-	r.NoError(err)
-
-	err = afero.WriteFile(fs, confPath, v1, 0644)
-	r.NoError(err)
-
-	_, v, err := FindConfig(fs, confPath)
-	r.NoError(err)
-	r.Equal(1, v)
-
-	err = Upgrade(fs, confPath)
-	r.NoError(err)
-
-	_, v, err = FindConfig(fs, confPath)
-	r.NoError(err)
-	r.Equal(2, v) // now upgraded
-}
diff --git a/migrations/upgrade_test.go b/migrations/upgrade_test.go
new file mode 100644
index 00000000..467b2cf5
--- /dev/null
+++ b/migrations/upgrade_test.go
@@ -0,0 +1,112 @@
+package migrations
+
+import (
+	"testing"
+
+	"github.com/chanzuckerberg/fogg/config"
+	"github.com/chanzuckerberg/fogg/util"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/require"
+)
+
+func TestUpgradeV2(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.yml"
+	conf := []byte(`{"version": 1}`)
+	upgradeMigration := VersionUpgradeMigration{}
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, conf, 0644)
+	r.Nil(err)
+
+	shouldRun, err := upgradeMigration.Guard(fs, confPath)
+	r.Equal(true, shouldRun)
+	r.Nil(err)
+
+	configFile, err := upgradeMigration.Migrate(fs, confPath)
+	r.Nil(err)
+	r.Equal("fogg.yml", configFile)
+
+	_, version, err := config.FindConfig(fs, configFile)
+	r.Nil(err)
+	r.Equal(2, version)
+}
+
+func TestV2DoNothing(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.yml"
+	conf := []byte(`{"version": 2}`)
+	upgradeMigration := VersionUpgradeMigration{}
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, conf, 0644)
+	r.Nil(err)
+
+	shouldRun, err := upgradeMigration.Guard(fs, confPath)
+	r.Equal(false, shouldRun)
+	r.Nil(err)
+
+	configFile, err := upgradeMigration.Migrate(fs, confPath)
+	r.Nil(err)
+	r.Equal("fogg.yml", configFile)
+
+	_, version, err := config.FindConfig(fs, configFile)
+	r.Nil(err)
+	r.Equal(2, version)
+}
+
+func TestUpgradeUnknownVersion(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.yml"
+	conf := []byte(`{"version": 100}`)
+	upgradeMigration := VersionUpgradeMigration{}
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, conf, 0644)
+	r.Nil(err)
+
+	shouldRun, err := upgradeMigration.Guard(fs, confPath)
+	r.Equal(false, shouldRun)
+	r.Error(err, "Config file version was not recognized")
+
+	configFile, err := upgradeMigration.Migrate(fs, confPath)
+	r.Error(err, "config version 100 unrecognized")
+	r.Equal(configFile, "fogg.yml")
+}
+
+func TestUpgradeV1(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.yml"
+	upgradeMigration := VersionUpgradeMigration{}
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	v1, err := util.TestFile("v1_full")
+	r.NoError(err)
+
+	err = afero.WriteFile(fs, confPath, v1, 0644)
+	r.NoError(err)
+
+	_, v, err := config.FindConfig(fs, confPath)
+	r.NoError(err)
+	r.Equal(1, v)
+
+	shouldRun, err := upgradeMigration.Guard(fs, confPath)
+	r.Nil(err)
+	r.Equal(true, shouldRun)
+
+	configFile, err := upgradeMigration.Migrate(fs, confPath)
+	r.NoError(err)
+
+	//configFile is passed to validate the Migrate returned string
+	_, v, err = config.FindConfig(fs, configFile)
+	r.NoError(err)
+	r.Equal(2, v) // now upgraded	
+}
diff --git a/migrations/yaml_test.go b/migrations/yaml_test.go
new file mode 100644
index 00000000..61dec32e
--- /dev/null
+++ b/migrations/yaml_test.go
@@ -0,0 +1,64 @@
+package migrations
+
+import (
+	"testing"
+
+	"github.com/chanzuckerberg/fogg/util"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/require"
+)
+
+func TestBasicYamlMigrate(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.json"
+	yamlMigration := JSONToYamlMigration{}
+	json := []byte(`{}`)
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, json, 0644)
+	r.Nil(err)
+
+	shouldRun, err := yamlMigration.Guard(fs, confPath)
+	r.Nil(err)
+	r.Equal(true,shouldRun)
+
+	configFile, err := yamlMigration.Migrate(fs, confPath)
+	r.Nil(err)
+	r.Equal("fogg.yml", configFile)
+}
+
+func TestGuardFileType(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.txt"
+	yamlMigration := JSONToYamlMigration{}
+	json := []byte(`{}`)
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, json, 0644)
+	r.Nil(err)
+
+	shouldRun, err := yamlMigration.Guard(fs, confPath)
+	r.Error(err, " Config file .txt was not recognized")
+	r.Equal(false, shouldRun)
+}
+
+func TestGuardFail(t *testing.T) {
+	r := require.New(t)
+	confPath := "fogg.yml"
+	yamlMigration := JSONToYamlMigration{}
+	json := []byte(`{}`)
+
+	fs, _, err := util.TestFs()
+	r.Nil(err)
+
+	err = afero.WriteFile(fs, confPath, json, 0644)
+	r.Nil(err)
+
+	shouldRun, err := yamlMigration.Guard(fs, confPath)
+	r.Nil(err)
+	r.Equal(false, shouldRun)
+}