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:
parent
f4e27ca480
commit
f1541adfad
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue