From 87853c893ef84a8658d9e0794cb3a1232c5dd621 Mon Sep 17 00:00:00 2001 From: "George L. Yermulnik" Date: Tue, 23 Apr 2024 16:55:50 +0300 Subject: [PATCH] feat: Support multiple TF version constraints from module and improve logging (#362) * feat: Support multiple TF version constaints from module * Improve logging on reading TF version constraint (moved from #361) * Align with https://github.com/hashicorp/terraform-config-inspect/blob/master/main.go#L35C2-L37 * Update `test-data/integration-tests/test_versiontf/version.tf` with multiple TF version constraints * Drop `required_providers` from `test-data/integration-tests/test_versiontf/version.tf` as `tfswitch` does not handle TF providers * Modified function to return an error in case of non matching constraints * Added testcase for non matching constraints * Moved failing testcase to skip-integration tests --------- Co-authored-by: Johannes Brunswicker Co-authored-by: Hugh Wells --- lib/param_parsing/parameters.go | 2 +- lib/param_parsing/versiontf.go | 61 +++++++++++++++++-- lib/param_parsing/versiontf_test.go | 27 ++++++-- lib/semver.go | 1 + .../test_versiontf/version.tf | 9 ++- .../version.tf | 11 ++++ 6 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 test-data/skip-integration-tests/test_versiontf_non_matching_constraints/version.tf diff --git a/lib/param_parsing/parameters.go b/lib/param_parsing/parameters.go index c196d647..8c5149f3 100644 --- a/lib/param_parsing/parameters.go +++ b/lib/param_parsing/parameters.go @@ -60,7 +60,7 @@ func GetParameters() Params { } else if terraformVersionFileExists(params) { params, err = GetParamsFromTerraformVersion(params) } else if isTerraformModule(params) { - params, _ = GetVersionFromVersionsTF(params) + params, err = GetVersionFromVersionsTF(params) } else if terraGruntFileExists(params) { params, err = GetVersionFromTerragrunt(params) } else { diff --git a/lib/param_parsing/versiontf.go b/lib/param_parsing/versiontf.go index 38852c4f..bcca9680 100644 --- a/lib/param_parsing/versiontf.go +++ b/lib/param_parsing/versiontf.go @@ -1,18 +1,69 @@ package param_parsing import ( + "fmt" + "os" + "path/filepath" + "strings" + "github.com/hashicorp/terraform-config-inspect/tfconfig" "github.com/warrensbox/terraform-switcher/lib" ) func GetVersionFromVersionsTF(params Params) (Params, error) { - logger.Infof("Reading version from terraform module at %q", params.ChDirPath) - module, err := tfconfig.LoadModule(params.ChDirPath) + var tfConstraints []string + var exactConstraints []string + + curDir, err := os.Getwd() + if err != nil { + logger.Fatalf("Could not get current working directory: %v", err) + } + + absPath := params.ChDirPath + if !filepath.IsAbs(params.ChDirPath) { + absPath, err = filepath.Abs(params.ChDirPath) + if err != nil { + logger.Fatalf("Could not derive absolute path to %q: %v", params.ChDirPath, err) + } + } + + relPath, err := filepath.Rel(curDir, absPath) if err != nil { - logger.Errorf("Could not load terraform module at %q", params.ChDirPath) - return params, err.Err() + logger.Fatalf("Could not derive relative path to %q: %v", params.ChDirPath, err) + } + + logger.Infof("Reading version from Terraform module at %q", relPath) + module, _ := tfconfig.LoadModule(params.ChDirPath) + if module.Diagnostics.HasErrors() { + logger.Fatalf("Could not load Terraform module at %q", params.ChDirPath) } - tfConstraint := module.RequiredCore[0] + + requiredVersions := module.RequiredCore + + for key := range requiredVersions { + tfConstraint := requiredVersions[key] + tfConstraintParts := strings.Fields(tfConstraint) + + if len(tfConstraintParts) > 2 { + logger.Fatalf("Invalid version constraint found: %q", tfConstraint) + } else if len(tfConstraintParts) == 1 { + exactConstraints = append(exactConstraints, tfConstraint) + tfConstraint = "= " + tfConstraintParts[0] + } + + if tfConstraintParts[0] == "=" { + exactConstraints = append(exactConstraints, tfConstraint) + } + + tfConstraints = append(tfConstraints, tfConstraint) + } + + if len(exactConstraints) > 0 && len(tfConstraints) > 1 { + return params, fmt.Errorf("exact constraint (%q) cannot be combined with other conditions", strings.Join(exactConstraints, ", ")) + } + + tfConstraint := strings.Join(tfConstraints, ", ") + version, err2 := lib.GetSemver(tfConstraint, params.MirrorURL) if err2 != nil { logger.Errorf("No version found matching %q", tfConstraint) diff --git a/lib/param_parsing/versiontf_test.go b/lib/param_parsing/versiontf_test.go index 245f9ec5..eaebafa2 100644 --- a/lib/param_parsing/versiontf_test.go +++ b/lib/param_parsing/versiontf_test.go @@ -4,20 +4,37 @@ import ( "fmt" "github.com/hashicorp/go-version" "github.com/warrensbox/terraform-switcher/lib" + "strings" "testing" ) -func TestGetVersionFromVersionsTF(t *testing.T) { +func TestGetVersionFromVersionsTF_matches_version(t *testing.T) { logger = lib.InitLogger("DEBUG") var params Params params = initParams(params) params.ChDirPath = "../../test-data/integration-tests/test_versiontf" params, _ = GetVersionFromVersionsTF(params) - v1, _ := version.NewVersion("1.0.0") - v2, _ := version.NewVersion("2.0.0") + v1, _ := version.NewVersion("1.0.5") actualVersion, _ := version.NewVersion(params.Version) - if !actualVersion.GreaterThanOrEqual(v1) || !actualVersion.LessThan(v2) { - t.Error("Determined version is not between 1.0.0 and 2.0.0") + if !actualVersion.Equal(v1) { + t.Error("Determined version is not 1.0.5") + } +} + +func TestGetVersionFromVersionsTF_non_matching_constraints(t *testing.T) { + logger = lib.InitLogger("DEBUG") + var params Params + params = initParams(params) + params.ChDirPath = "../../test-data/skip-integration-tests/test_versiontf_non_matching_constraints" + params, err := GetVersionFromVersionsTF(params) + if err == nil { + t.Error("Expected error but got nil") + } else { + expected := "exact constraint (" + expected2 := ") cannot be combined with other conditions" + if !strings.Contains(fmt.Sprint(err), expected) || !strings.Contains(fmt.Sprint(err), expected2) { + t.Errorf("Expected error string containing %q and %q. Got %q", expected, expected2, err) + } } } diff --git a/lib/semver.go b/lib/semver.go index fa8f138a..2f35bac5 100644 --- a/lib/semver.go +++ b/lib/semver.go @@ -46,6 +46,7 @@ func SemVerParser(tfconstraint *string, tflist []string) (string, error) { } } } + return "", fmt.Errorf("did not find version matching constraint: %s", *tfconstraint) } diff --git a/test-data/integration-tests/test_versiontf/version.tf b/test-data/integration-tests/test_versiontf/version.tf index 07c2eea1..0e80ebd4 100644 --- a/test-data/integration-tests/test_versiontf/version.tf +++ b/test-data/integration-tests/test_versiontf/version.tf @@ -1,8 +1,7 @@ terraform { required_version = "~> 1.0.0" +} - required_providers { - aws = ">= 2.52.0" - kubernetes = ">= 1.11.1" - } -} \ No newline at end of file +terraform { + required_version = "<= 1.0.5" +} diff --git a/test-data/skip-integration-tests/test_versiontf_non_matching_constraints/version.tf b/test-data/skip-integration-tests/test_versiontf_non_matching_constraints/version.tf new file mode 100644 index 00000000..c8ee7814 --- /dev/null +++ b/test-data/skip-integration-tests/test_versiontf_non_matching_constraints/version.tf @@ -0,0 +1,11 @@ +terraform { + required_version = "~> 1.0.0" +} + +terraform { + required_version = "= 1.0.5" +} + +terraform { + required_version = "<= 1.0.4" +}