From 456135d2ab4f6c14f9af5133ae879545d5599816 Mon Sep 17 00:00:00 2001 From: Ryan King Date: Wed, 12 Sep 2018 09:11:12 -0700 Subject: [PATCH 1/3] parse our version strings --- util/version.go | 19 +++++++++++++++++++ util/version_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/util/version.go b/util/version.go index 6263166dd..6b62e670c 100644 --- a/util/version.go +++ b/util/version.go @@ -3,6 +3,7 @@ package util import ( "fmt" "strconv" + "strings" "github.com/blang/semver" "github.com/pkg/errors" @@ -39,6 +40,24 @@ func VersionCacheKey() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) } +func ParseVersion(version string) (semver.Version, string, bool) { + var dirty bool + var sha string + v := version + if strings.HasSuffix(v, "-dirty") { + dirty = true + v = strings.TrimSuffix(v, "-dirty") + } + if strings.Contains(v, "-") { + tmp := strings.Split(v, "-") + v = tmp[0] + sha = tmp[1] + } + + semVersion, _ := semver.Parse(v) + return semVersion, sha, dirty +} + func versionString(version, sha string, release, dirty bool) string { if release { return version diff --git a/util/version_test.go b/util/version_test.go index e062dcef9..e9da83bd4 100644 --- a/util/version_test.go +++ b/util/version_test.go @@ -3,6 +3,7 @@ package util import ( "testing" + "github.com/blang/semver" "github.com/stretchr/testify/assert" ) @@ -17,3 +18,29 @@ func TestVersionString(t *testing.T) { assert.Equal(t, "0.1.0-abcdef-dirty", s) } + +func TestParse(t *testing.T) { + a := assert.New(t) + + testCases := []struct { + input string + version string + sha string + dirty bool + }{ + {"0.1.0", "0.1.0", "", false}, + {"0.1.0-abcdef", "0.1.0", "abcdef", false}, + {"0.1.0-abcdef-dirty", "0.1.0", "abcdef", true}, + } + for _, tc := range testCases { + t.Run("", func(t *testing.T) { + v, sha, dirty := ParseVersion(tc.input) + semVersion, e := semver.Parse(tc.version) + a.NoError(e) + a.Equal(semVersion, v) + a.Equal(tc.sha, sha) + a.Equal(tc.dirty, dirty) + }) + } + +} From 709a028430bcfbccf6cc5d3f0f3b51de3f293b86 Mon Sep 17 00:00:00 2001 From: Ryan King Date: Wed, 12 Sep 2018 10:12:22 -0700 Subject: [PATCH 2/3] check for version mis-match on apply If --upgrade/-u is not supplied, apply will fail. Currently the UX for this error is bad. After the PR I will do a pass at better error UX. --- apply/apply.go | 42 +++++++++++++++++++++++++++++++++++++++++- apply/apply_test.go | 44 +++++++++++++++++++++++++++++++++++++++++++- cmd/apply.go | 8 +++++++- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/apply/apply.go b/apply/apply.go index 3a54a22a9..28df5cd6e 100644 --- a/apply/apply.go +++ b/apply/apply.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "io/ioutil" "net/url" "os" "path/filepath" @@ -26,7 +27,14 @@ import ( const rootPath = "terraform" -func Apply(fs afero.Fs, conf *config.Config, tmp *templates.T) error { +func Apply(fs afero.Fs, conf *config.Config, tmp *templates.T, upgrade bool) error { + if !upgrade { + toolVersion, _ := util.VersionString() + versionChange, _ := checkToolVersions(fs, toolVersion) + if versionChange { + return fmt.Errorf("fogg version (%s) is different than version used to manage repo. To upgrade add --upgrade.", toolVersion) + } + } p, err := plan.Eval(conf, false) if err != nil { return errors.Wrap(err, "unable to evaluate plan") @@ -56,6 +64,38 @@ func Apply(fs afero.Fs, conf *config.Config, tmp *templates.T) error { return errors.Wrap(e, "unable to apply modules") } +func checkToolVersions(fs afero.Fs, current string) (bool, error) { + f, e := fs.Open(".fogg-version") + if e != nil { + return false, errors.Wrap(e, "unable to open .fogg-version file") + } + reader := io.ReadCloser(f) + defer reader.Close() + + b, e := ioutil.ReadAll(reader) + if e != nil { + return false, errors.Wrap(e, "unable to read .fogg-version file") + } + repoVersion := string(b) + changed, e := versionIsChanged(repoVersion, current) + return changed, e +} + +func versionIsChanged(repo string, tool string) (bool, error) { + repoVersion, repoSha, repoDirty := util.ParseVersion(repo) + toolVersion, toolSha, toolDirty := util.ParseVersion(tool) + + if repoDirty || toolDirty { + return true, nil + } + + if (repoSha != "" || toolSha != "") && repoSha != toolSha { + return true, nil + } + + return toolVersion.NE(repoVersion), nil +} + func applyRepo(fs afero.Fs, p *plan.Plan, repoTemplates *packr.Box) error { e := applyTree(fs, repoTemplates, "", p) if e != nil { diff --git a/apply/apply_test.go b/apply/apply_test.go index d90840136..53c1916cc 100644 --- a/apply/apply_test.go +++ b/apply/apply_test.go @@ -158,7 +158,7 @@ func TestApplySmokeTest(t *testing.T) { c, e := config.ReadConfig(ioutil.NopCloser(strings.NewReader(json))) assert.Nil(t, e) - e = Apply(fs, c, templates.Templates) + e = Apply(fs, c, templates.Templates, false) assert.Nil(t, e) } @@ -274,6 +274,48 @@ func TestCalculateLocalPath(t *testing.T) { } } +var versionTests = []struct { + current string + tool string + result bool +}{ + {"0.0.0", "0.1.0", true}, + {"0.1.0", "0.0.0", true}, + {"0.1.0", "0.1.0", false}, + {"0.1.0", "0.1.0-abcdef", true}, + {"0.1.0", "0.1.0-abcdef-dirty", true}, + {"0.1.0-abcdef-dirty", "0.1.0", true}, + {"0.1.0-abc", "0.1.0-def", true}, + {"0.1.0-abc", "0.1.0-abc", false}, +} + +func TestCheckToolVersions(t *testing.T) { + a := assert.New(t) + + for _, tc := range versionTests { + t.Run("", func(t *testing.T) { + fs := afero.NewMemMapFs() + writeFile(fs, ".fogg-version", tc.current) + + v, e := checkToolVersions(fs, tc.tool) + a.NoError(e) + a.Equal(tc.result, v) + }) + } +} + +func TestVersionIsChanged(t *testing.T) { + a := assert.New(t) + + for _, test := range versionTests { + t.Run("", func(t *testing.T) { + b, e := versionIsChanged(test.current, test.tool) + a.NoError(e) + a.Equal(test.result, b) + }) + } +} + func readFile(fs afero.Fs, path string) (string, error) { f, e := fs.Open(path) if e != nil { diff --git a/cmd/apply.go b/cmd/apply.go index 0d1d7233e..4d68b4058 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -13,6 +13,7 @@ import ( func init() { applyCmd.Flags().StringP("config", "c", "fogg.json", "Use this to override the fogg config file.") applyCmd.Flags().BoolP("verbose", "v", false, "use this to turn on verbose output") + applyCmd.Flags().BoolP("upgrade", "u", false, "use this when running a new version of fogg") rootCmd.AddCommand(applyCmd) } @@ -47,6 +48,11 @@ var applyCmd = &cobra.Command{ log.Panic(e) } + upgrade, e := cmd.Flags().GetBool("upgrade") + if e != nil { + log.Panic(e) + } + // check that we are at root of initialized git repo openGitOrExit(pwd) @@ -55,7 +61,7 @@ var applyCmd = &cobra.Command{ exitOnConfigErrors(err) // apply - e = apply.Apply(fs, config, templates.Templates) + e = apply.Apply(fs, config, templates.Templates, upgrade) if e != nil { log.Panic(e) } From 6485e0bee09fd8be72e5ab3a2d32f29724bc0120 Mon Sep 17 00:00:00 2001 From: Ryan King Date: Wed, 12 Sep 2018 10:18:00 -0700 Subject: [PATCH 3/3] better error msg --- apply/apply.go | 12 ++++++------ apply/apply_test.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apply/apply.go b/apply/apply.go index 28df5cd6e..951739106 100644 --- a/apply/apply.go +++ b/apply/apply.go @@ -30,9 +30,9 @@ const rootPath = "terraform" func Apply(fs afero.Fs, conf *config.Config, tmp *templates.T, upgrade bool) error { if !upgrade { toolVersion, _ := util.VersionString() - versionChange, _ := checkToolVersions(fs, toolVersion) + versionChange, repoVersion, _ := checkToolVersions(fs, toolVersion) if versionChange { - return fmt.Errorf("fogg version (%s) is different than version used to manage repo. To upgrade add --upgrade.", toolVersion) + return fmt.Errorf("fogg version (%s) is different than version currently used to manage repo (%s). To upgrade add --upgrade.", toolVersion, repoVersion) } } p, err := plan.Eval(conf, false) @@ -64,21 +64,21 @@ func Apply(fs afero.Fs, conf *config.Config, tmp *templates.T, upgrade bool) err return errors.Wrap(e, "unable to apply modules") } -func checkToolVersions(fs afero.Fs, current string) (bool, error) { +func checkToolVersions(fs afero.Fs, current string) (bool, string, error) { f, e := fs.Open(".fogg-version") if e != nil { - return false, errors.Wrap(e, "unable to open .fogg-version file") + return false, "", errors.Wrap(e, "unable to open .fogg-version file") } reader := io.ReadCloser(f) defer reader.Close() b, e := ioutil.ReadAll(reader) if e != nil { - return false, errors.Wrap(e, "unable to read .fogg-version file") + return false, "", errors.Wrap(e, "unable to read .fogg-version file") } repoVersion := string(b) changed, e := versionIsChanged(repoVersion, current) - return changed, e + return changed, repoVersion, e } func versionIsChanged(repo string, tool string) (bool, error) { diff --git a/apply/apply_test.go b/apply/apply_test.go index 53c1916cc..dc34f7b0f 100644 --- a/apply/apply_test.go +++ b/apply/apply_test.go @@ -297,7 +297,7 @@ func TestCheckToolVersions(t *testing.T) { fs := afero.NewMemMapFs() writeFile(fs, ".fogg-version", tc.current) - v, e := checkToolVersions(fs, tc.tool) + v, _, e := checkToolVersions(fs, tc.tool) a.NoError(e) a.Equal(tc.result, v) })