Skip to content

Commit

Permalink
Use the apparentlymart/go-versions library to parse module constraints (
Browse files Browse the repository at this point in the history
#32377)

* Use the apparentlymart/go-versions library to parse module constraints

* goimports

* Update comments, and parse versions carefully

* add acceptance tests to verify behaviour of partial matches

* goimports
  • Loading branch information
liamcervante committed Dec 14, 2022
1 parent a9230c9 commit 6af6540
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 3 deletions.
44 changes: 41 additions & 3 deletions internal/initwd/module_install.go
Expand Up @@ -10,7 +10,9 @@ import (
"path/filepath"
"strings"

"github.com/apparentlymart/go-versions/versions"
version "github.com/hashicorp/go-version"

"github.com/hashicorp/terraform-config-inspect/tfconfig"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/earlyconfig"
Expand Down Expand Up @@ -387,9 +389,45 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc

// If we've found a pre-release version then we'll ignore it unless
// it was exactly requested.
if v.Prerelease() != "" && req.VersionConstraints.String() != v.String() {
log.Printf("[TRACE] ModuleInstaller: %s ignoring %s because it is a pre-release and was not requested exactly", key, v)
continue
//
// The prerelease checking will be handled by a different library for
// 2 reasons. First, this other library automatically includes the
// "prerelease versions must be exactly requested" behaviour that we are
// looking for. Second, this other library is used to handle all version
// constraints for the provider logic and this is the first step to
// making the module and provider version logic match.
if v.Prerelease() != "" {
// At this point all versions published by the module with
// prerelease metadata will be checked. Users may not have even
// requested this prerelease so don't print lots of unnecessary #
// warnings.
acceptableVersions, err := versions.MeetingConstraintsString(req.VersionConstraints.String())
if err != nil {
log.Printf("[WARN] ModuleInstaller: %s ignoring %s because the version constraints (%s) could not be parsed: %s", key, v, req.VersionConstraints.String(), err.Error())
continue
}

// Validate the version is also readable by the other versions
// library.
version, err := versions.ParseVersion(v.String())
if err != nil {
log.Printf("[WARN] ModuleInstaller: %s ignoring %s because the version (%s) reported by the module could not be parsed: %s", key, v, v.String(), err.Error())
continue
}

// Finally, check if the prerelease is acceptable to version. As
// highlighted previously, we go through all of this because the
// apparentlymart/go-versions library handles prerelease constraints
// in the apporach we want to.
if !acceptableVersions.Has(version) {
log.Printf("[TRACE] ModuleInstaller: %s ignoring %s because it is a pre-release and was not requested exactly", key, v)
continue
}

// If we reach here, it means this prerelease version was exactly
// requested according to the extra constraints of this library.
// We fall through and allow the other library to also validate it
// for consistency.
}

if latestVersion == nil || v.GreaterThan(latestVersion) {
Expand Down
48 changes: 48 additions & 0 deletions internal/initwd/module_install_test.go
Expand Up @@ -178,6 +178,54 @@ func TestModuleInstaller_explicitPackageBoundary(t *testing.T) {
}
}

func TestModuleInstaller_ExactMatchPrerelease(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
t.Skip("this test accesses registry.terraform.io and github.com; set TF_ACC=1 to run it")
}

fixtureDir := filepath.Clean("testdata/prerelease-version-constraint-match")
dir, done := tempChdir(t, fixtureDir)
defer done()

hooks := &testInstallHooks{}

modulesDir := filepath.Join(dir, ".terraform/modules")
inst := NewModuleInstaller(modulesDir, registry.NewClient(nil, nil))
cfg, diags := inst.InstallModules(context.Background(), ".", false, hooks)

if diags.HasErrors() {
t.Fatalf("found unexpected errors: %s", diags.Err())
}

if !cfg.Children["acctest_exact"].Version.Equal(version.Must(version.NewVersion("v0.0.3-alpha.1"))) {
t.Fatalf("expected version %s but found version %s", "v0.0.3-alpha.1", cfg.Version.String())
}
}

func TestModuleInstaller_PartialMatchPrerelease(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
t.Skip("this test accesses registry.terraform.io and github.com; set TF_ACC=1 to run it")
}

fixtureDir := filepath.Clean("testdata/prerelease-version-constraint")
dir, done := tempChdir(t, fixtureDir)
defer done()

hooks := &testInstallHooks{}

modulesDir := filepath.Join(dir, ".terraform/modules")
inst := NewModuleInstaller(modulesDir, registry.NewClient(nil, nil))
cfg, diags := inst.InstallModules(context.Background(), ".", false, hooks)

if diags.HasErrors() {
t.Fatalf("found unexpected errors: %s", diags.Err())
}

if !cfg.Children["acctest_partial"].Version.Equal(version.Must(version.NewVersion("v0.0.2"))) {
t.Fatalf("expected version %s but found version %s", "v0.0.2", cfg.Version.String())
}
}

func TestModuleInstaller_invalid_version_constraint_error(t *testing.T) {
fixtureDir := filepath.Clean("testdata/invalid-version-constraint")
dir, done := tempChdir(t, fixtureDir)
Expand Down
@@ -0,0 +1,7 @@
# We expect this test to download the requested version because it is an exact
# match for a prerelease version.

module "acctest_exact" {
source = "hashicorp/module-installer-acctest/aws"
version = "=0.0.3-alpha.1"
}
@@ -0,0 +1,8 @@
# We expect this test to download the version 0.0.2, the one before the
# specified version even with the equality because the specified version is a
# prerelease.

module "acctest_partial" {
source = "hashicorp/module-installer-acctest/aws"
version = "<=0.0.3-alpha.1"
}

0 comments on commit 6af6540

Please sign in to comment.