internal/depsfile: Introduce the concept of "non-lockable" providers

It doesn't make sense for a built-in provider to appear in a lock file
because built-in providers have no version independent of the version of
Terraform they are compiled into.

We also exclude legacy providers here, because they were supported only
as a transitional aid to enable the Terraform 0.13 upgrade process and
are not intended for explicit selection.

The provider installer will, once it's updated to understand dependency
locking, use this concept to decide which subset of its selections to
record in the dependency lock file for reference for future installation
requests.
This commit is contained in:
Martin Atkins 2020-09-04 14:42:04 -07:00
parent 98e2e69abb
commit 773dd56b42
3 changed files with 50 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package depsfile
import (
"fmt"
"sort"
"github.com/hashicorp/terraform/addrs"
@ -52,7 +53,16 @@ func (l *Locks) Provider(addr addrs.Provider) *ProviderLock {
// SetProvider returns the newly-created provider lock object, which
// invalidates any ProviderLock object previously returned from Provider or
// SetProvider for the given provider address.
//
// Only lockable providers can be passed to this method. If you pass a
// non-lockable provider address then this function will panic. Use
// function ProviderIsLockable to determine whether a particular provider
// should participate in the version locking mechanism.
func (l *Locks) SetProvider(addr addrs.Provider, version getproviders.Version, constraints getproviders.VersionConstraints, hashes map[getproviders.Platform][]string) *ProviderLock {
if !ProviderIsLockable(addr) {
panic(fmt.Sprintf("Locks.SetProvider with non-lockable provider %s", addr))
}
// Normalize the hash lists into a consistent order.
for _, slice := range hashes {
sort.Strings(slice)
@ -68,6 +78,15 @@ func (l *Locks) SetProvider(addr addrs.Provider, version getproviders.Version, c
return new
}
// ProviderIsLockable returns true if the given provider is eligible for
// version locking.
//
// Currently, all providers except builtin and legacy providers are eligible
// for locking.
func ProviderIsLockable(addr addrs.Provider) bool {
return !(addr.IsBuiltIn() || addr.IsLegacy())
}
// Sources returns the source code of the file the receiver was generated from,
// or an empty map if the receiver wasn't generated from a file.
//

View File

@ -237,6 +237,27 @@ func decodeProviderLockFromHCL(block *hcl.Block) (*ProviderLock, tfdiags.Diagnos
})
return nil, diags
}
if !ProviderIsLockable(addr) {
if addr.IsBuiltIn() {
// A specialized error for built-in providers, because we have an
// explicit explanation for why those are not allowed.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid provider source address",
Detail: fmt.Sprintf("Cannot lock a version for built-in provider %s. Built-in providers are bundled inside Terraform itself, so you can't select a version for them independently of the Terraform release you are currently running.", addr),
Subject: block.LabelRanges[0].Ptr(),
})
return nil, diags
}
// Otherwise, we'll use a generic error message.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid provider source address",
Detail: fmt.Sprintf("Provider source address %s is a special provider that is not eligible for dependency locking.", addr),
Subject: block.LabelRanges[0].Ptr(),
})
return nil, diags
}
if canonAddr := addr.String(); canonAddr != rawAddr {
// We also require the provider addresses in the lock file to be
// written in fully-qualified canonical form, so that it's totally

View File

@ -32,3 +32,13 @@ provider "this/one/okay" {
provider "this/one/okay" { # ERROR: Duplicate provider lock
}
# Legacy providers are not allowed, because they existed only to
# support the Terraform 0.13 upgrade process.
provider "registry.terraform.io/-/null" { # ERROR: Invalid provider source address
}
# Built-in providers are not allowed, because they are not versioned
# independently of the Terraform CLI release they are embedded in.
provider "terraform.io/builtin/foo" { # ERROR: Invalid provider source address
}