internal: Fix lockfile constraint output for 1.2.*
If a configuration requires a partial provider version (with some parts unspecified), Terraform considers this as a constrained-to-zero version. For example, a version constraint of 1.2 will result in an attempt to install version 1.2.0, even if 1.2.1 is available. When writing the dependency locks file, we previously would write 1.2.*, as this is the in-memory representation of 1.2. This would then cause an error on re-reading the locks file, as this is not a valid constraint format. Instead, we now explicitly convert the constraint to its zero-filled representation before writing the locks file. This ensures that it correctly round-trips. Because this change is made in getproviders.VersionConstraintsString, it also affects the output of the providers sub-command.
This commit is contained in:
parent
cdebf2820d
commit
9576a5b2d8
|
@ -114,7 +114,7 @@ func TestProviders_modules(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wantOutput := []string{
|
wantOutput := []string{
|
||||||
"provider[registry.terraform.io/hashicorp/foo] 1.0.*", // from required_providers
|
"provider[registry.terraform.io/hashicorp/foo] 1.0.0", // from required_providers
|
||||||
"provider[registry.terraform.io/hashicorp/bar] 2.0.0", // from provider config
|
"provider[registry.terraform.io/hashicorp/bar] 2.0.0", // from provider config
|
||||||
"── module.kiddo", // tree node for child module
|
"── module.kiddo", // tree node for child module
|
||||||
"provider[registry.terraform.io/hashicorp/baz]", // implied by a resource in the child module
|
"provider[registry.terraform.io/hashicorp/baz]", // implied by a resource in the child module
|
||||||
|
@ -151,7 +151,7 @@ func TestProviders_state(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wantOutput := []string{
|
wantOutput := []string{
|
||||||
"provider[registry.terraform.io/hashicorp/foo] 1.0.*", // from required_providers
|
"provider[registry.terraform.io/hashicorp/foo] 1.0.0", // from required_providers
|
||||||
"provider[registry.terraform.io/hashicorp/bar] 2.0.0", // from a provider config block
|
"provider[registry.terraform.io/hashicorp/bar] 2.0.0", // from a provider config block
|
||||||
"Providers required by state", // header for state providers
|
"Providers required by state", // header for state providers
|
||||||
"provider[registry.terraform.io/hashicorp/baz]", // from a resouce in state (only)
|
"provider[registry.terraform.io/hashicorp/baz]", // from a resouce in state (only)
|
||||||
|
|
|
@ -165,10 +165,12 @@ func TestSaveLocksToFile(t *testing.T) {
|
||||||
fooProvider := addrs.MustParseProviderSourceString("test/foo")
|
fooProvider := addrs.MustParseProviderSourceString("test/foo")
|
||||||
barProvider := addrs.MustParseProviderSourceString("test/bar")
|
barProvider := addrs.MustParseProviderSourceString("test/bar")
|
||||||
bazProvider := addrs.MustParseProviderSourceString("test/baz")
|
bazProvider := addrs.MustParseProviderSourceString("test/baz")
|
||||||
|
booProvider := addrs.MustParseProviderSourceString("test/boo")
|
||||||
oneDotOh := getproviders.MustParseVersion("1.0.0")
|
oneDotOh := getproviders.MustParseVersion("1.0.0")
|
||||||
oneDotTwo := getproviders.MustParseVersion("1.2.0")
|
oneDotTwo := getproviders.MustParseVersion("1.2.0")
|
||||||
atLeastOneDotOh := getproviders.MustParseVersionConstraints(">= 1.0.0")
|
atLeastOneDotOh := getproviders.MustParseVersionConstraints(">= 1.0.0")
|
||||||
pessimisticOneDotOh := getproviders.MustParseVersionConstraints("~> 1")
|
pessimisticOneDotOh := getproviders.MustParseVersionConstraints("~> 1")
|
||||||
|
abbreviatedOneDotTwo := getproviders.MustParseVersionConstraints("1.2")
|
||||||
hashes := []getproviders.Hash{
|
hashes := []getproviders.Hash{
|
||||||
getproviders.MustParseHash("test:cccccccccccccccccccccccccccccccccccccccccccccccc"),
|
getproviders.MustParseHash("test:cccccccccccccccccccccccccccccccccccccccccccccccc"),
|
||||||
getproviders.MustParseHash("test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
|
getproviders.MustParseHash("test:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
|
||||||
|
@ -177,6 +179,7 @@ func TestSaveLocksToFile(t *testing.T) {
|
||||||
locks.SetProvider(fooProvider, oneDotOh, atLeastOneDotOh, hashes)
|
locks.SetProvider(fooProvider, oneDotOh, atLeastOneDotOh, hashes)
|
||||||
locks.SetProvider(barProvider, oneDotTwo, pessimisticOneDotOh, nil)
|
locks.SetProvider(barProvider, oneDotTwo, pessimisticOneDotOh, nil)
|
||||||
locks.SetProvider(bazProvider, oneDotTwo, nil, nil)
|
locks.SetProvider(bazProvider, oneDotTwo, nil, nil)
|
||||||
|
locks.SetProvider(booProvider, oneDotTwo, abbreviatedOneDotTwo, nil)
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "terraform-internal-depsfile-savelockstofile")
|
dir, err := ioutil.TempDir("", "terraform-internal-depsfile-savelockstofile")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -207,6 +210,11 @@ provider "registry.terraform.io/test/baz" {
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider "registry.terraform.io/test/boo" {
|
||||||
|
version = "1.2.0"
|
||||||
|
constraints = "1.2.0"
|
||||||
|
}
|
||||||
|
|
||||||
provider "registry.terraform.io/test/foo" {
|
provider "registry.terraform.io/test/foo" {
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
constraints = ">= 1.0.0"
|
constraints = ">= 1.0.0"
|
||||||
|
|
|
@ -422,27 +422,34 @@ func VersionConstraintsString(spec VersionConstraints) string {
|
||||||
b.WriteString("??? ")
|
b.WriteString("??? ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The parser allows writing abbreviated version (such as 2) which
|
||||||
|
// end up being represented in memory with trailing unconstrained parts
|
||||||
|
// (for example 2.*.*). For the purpose of serialization with Ruby
|
||||||
|
// style syntax, these unconstrained parts can all be represented as 0
|
||||||
|
// with no loss of meaning, so we make that conversion here.
|
||||||
|
//
|
||||||
|
// This is possible because we use a different constraint operator to
|
||||||
|
// distinguish between the two types of pessimistic constraint:
|
||||||
|
// minor-only and patch-only. For minor-only constraints, we always
|
||||||
|
// want to display only the major and minor version components, so we
|
||||||
|
// special-case that operator below.
|
||||||
|
//
|
||||||
|
// One final edge case is a minor-only constraint specified with only
|
||||||
|
// the major version, such as ~> 2. We treat this the same as ~> 2.0,
|
||||||
|
// because a major-only pessimistic constraint does not exist: it is
|
||||||
|
// logically identical to >= 2.0.0.
|
||||||
|
boundary := sel.Boundary.ConstrainToZero()
|
||||||
if sel.Operator == constraints.OpGreaterThanOrEqualMinorOnly {
|
if sel.Operator == constraints.OpGreaterThanOrEqualMinorOnly {
|
||||||
// The minor-pessimistic syntax uses only two version components.
|
// The minor-pessimistic syntax uses only two version components.
|
||||||
if sel.Boundary.Minor.Unconstrained {
|
fmt.Fprintf(&b, "%s.%s", boundary.Major, boundary.Minor)
|
||||||
// The parser allows writing ~> 2, which ends up being
|
|
||||||
// represented in memory as ~> 2.* because the minor
|
|
||||||
// version is unconstrained, but that's not really any
|
|
||||||
// different than saying 2.0 and so we'll prefer that in
|
|
||||||
// our serialization in order to be clearer about how we
|
|
||||||
// understood the version constraint.
|
|
||||||
fmt.Fprintf(&b, "%s.0", sel.Boundary.Major)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(&b, "%s.%s", sel.Boundary.Major, sel.Boundary.Minor)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "%s.%s.%s", sel.Boundary.Major, sel.Boundary.Minor, sel.Boundary.Patch)
|
fmt.Fprintf(&b, "%s.%s.%s", boundary.Major, boundary.Minor, boundary.Patch)
|
||||||
}
|
}
|
||||||
if sel.Boundary.Prerelease != "" {
|
if sel.Boundary.Prerelease != "" {
|
||||||
b.WriteString("-" + sel.Boundary.Prerelease)
|
b.WriteString("-" + boundary.Prerelease)
|
||||||
}
|
}
|
||||||
if sel.Boundary.Metadata != "" {
|
if sel.Boundary.Metadata != "" {
|
||||||
b.WriteString("+" + sel.Boundary.Metadata)
|
b.WriteString("+" + boundary.Metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
|
|
Loading…
Reference in New Issue