Skip to content

Commit

Permalink
Ensure matching modules with metadata when requested (#21640)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pselle authored Jun 7, 2019
1 parent f4e27ca commit f1541ad
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
34 changes: 34 additions & 0 deletions config/module/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package module
import (
"errors"
"fmt"
"regexp"
"sort"
"strings"

version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/registry/response"
)

const anyVersion = ">=0.0.0"

var explicitEqualityConstraint = regexp.MustCompile("^=[0-9]")

// return the newest version that satisfies the provided constraint
func newest(versions []string, constraint string) (string, error) {
if constraint == "" {
Expand All @@ -21,6 +25,30 @@ func newest(versions []string, constraint string) (string, error) {
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) {
case 0:
return "", errors.New("no versions found")
Expand Down Expand Up @@ -58,6 +86,12 @@ func newest(versions []string, constraint string) (string, error) {
continue
}
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
}
}
Expand Down
30 changes: 30 additions & 0 deletions config/module/versions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,33 @@ func TestNewestInvalidModuleVersion(t *testing.T) {
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")
}
}

0 comments on commit f1541ad

Please sign in to comment.