Ensure matching modules with metadata when requested (#21640)

When a constraint is defined including build metadata, go-version ignores
this, and if a user explicitly requests a build version, this will now return that version.
Also covers cases when a user adds build metadata to a non-equality constraint,
or in a constraint with multiple conditions (ex. >0.8.0+abc, <1.0.0), and gives
and error when this occurs.
This commit is contained in:
Pam Selle 2019-06-07 14:29:05 -04:00 committed by GitHub
parent f4e27ca480
commit f1541adfad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 0 deletions

View File

@ -3,7 +3,9 @@ package module
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"sort" "sort"
"strings"
version "github.com/hashicorp/go-version" version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/registry/response" "github.com/hashicorp/terraform/registry/response"
@ -11,6 +13,8 @@ import (
const anyVersion = ">=0.0.0" const anyVersion = ">=0.0.0"
var explicitEqualityConstraint = regexp.MustCompile("^=[0-9]")
// return the newest version that satisfies the provided constraint // return the newest version that satisfies the provided constraint
func newest(versions []string, constraint string) (string, error) { func newest(versions []string, constraint string) (string, error) {
if constraint == "" { if constraint == "" {
@ -21,6 +25,30 @@ func newest(versions []string, constraint string) (string, error) {
return "", err return "", err
} }
// Find any build metadata in the constraints, and
// store whether the constraint is an explicit equality that
// contains a build metadata requirement, so we can return a specific,
// if requested, build metadata version
var constraintMetas []string
var equalsConstraint bool
for i := range cs {
constraintMeta := strings.SplitAfterN(cs[i].String(), "+", 2)
if len(constraintMeta) > 1 {
constraintMetas = append(constraintMetas, constraintMeta[1])
}
}
if len(cs) == 1 {
equalsConstraint = explicitEqualityConstraint.MatchString(cs.String())
}
// If the version string includes metadata, this is valid in go-version,
// However, it's confusing as to what expected behavior should be,
// so give an error so the user can do something more logical
if (len(cs) > 1 || !equalsConstraint) && len(constraintMetas) > 0 {
return "", fmt.Errorf("Constraints including build metadata must have explicit equality, or are otherwise too ambiguous: %s", cs.String())
}
switch len(versions) { switch len(versions) {
case 0: case 0:
return "", errors.New("no versions found") return "", errors.New("no versions found")
@ -58,6 +86,12 @@ func newest(versions []string, constraint string) (string, error) {
continue continue
} }
if cs.Check(v) { if cs.Check(v) {
// Constraint has metadata and is explicit equality
if equalsConstraint && len(constraintMetas) > 0 {
if constraintMetas[0] != v.Metadata() {
continue
}
}
return versions[i], nil return versions[i], nil
} }
} }

View File

@ -58,3 +58,33 @@ func TestNewestInvalidModuleVersion(t *testing.T) {
t.Fatalf("expected version %q, got %q", expected, m.Version) t.Fatalf("expected version %q, got %q", expected, m.Version)
} }
} }
func TestNewestModulesWithMetadata(t *testing.T) {
mpv := &response.ModuleProviderVersions{
Source: "registry/test/module",
Versions: []*response.ModuleVersion{
{Version: "0.9.0"},
{Version: "0.9.0+def"},
{Version: "0.9.0+abc"},
{Version: "0.9.0+xyz"},
},
}
// with metadata and explicit version request
expected := "0.9.0+def"
m, _ := newestVersion(mpv.Versions, "=0.9.0+def")
if m.Version != expected {
t.Fatalf("expected version %q, got %q", expected, m.Version)
}
// respect explicit equality, but >/</~, or metadata in multiple constraints, will give an error
_, err := newestVersion(mpv.Versions, "~>0.9.0+abc")
if err == nil {
t.Fatalf("expected an error, but did not get one")
}
_, err = newestVersion(mpv.Versions, ">0.8.0+abc, <1.0.0")
if err == nil {
t.Fatalf("expected an error, but did not get one")
}
}