Skip to content

Commit

Permalink
addrs: ImpliedProviderForUnqualifiedType function
Browse files Browse the repository at this point in the history
This encapsulates the logic for selecting an implied FQN for an
unqualified type name, which could either come from a local name used in
a module without specifying an explicit source for it or from the prefix
of a resource type on a resource that doesn't explicitly set "provider".

This replaces the previous behavior of just directly calling
NewDefaultProvider everywhere so that we can use a different implication
for the local name "terraform", to refer to the built-in terraform
provider rather than the stale one that's on registry.terraform.io for
compatibility with other Terraform versions.
  • Loading branch information
apparentlymart committed Apr 6, 2020
1 parent 27a7940 commit 7caf0b9
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 12 deletions.
24 changes: 24 additions & 0 deletions addrs/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,30 @@ func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider
}
}

// ImpliedProviderForUnqualifiedType represents the rules for inferring what
// provider FQN a user intended when only a naked type name is available.
//
// For all except the type name "terraform" this returns a so-called "default"
// provider, which is under the registry.terraform.io/hashicorp/ namespace.
//
// As a special case, the string "terraform" maps to
// "terraform.io/builtin/terraform" because that is the more likely user
// intent than the now-unmaintained "registry.terraform.io/hashicorp/terraform"
// which remains only for compatibility with older Terraform versions.
func ImpliedProviderForUnqualifiedType(typeName string) Provider {
switch typeName {
case "terraform":
// Note for future maintainers: any additional strings we add here
// as implied to be builtin must never also be use as provider names
// in the registry.terraform.io/hashicorp/... namespace, because
// otherwise older versions of Terraform could implicitly select
// the registry name instead of the internal one.
return NewBuiltInProvider(typeName)
default:
return NewDefaultProvider(typeName)
}
}

// NewDefaultProvider returns the default address of a HashiCorp-maintained,
// Registry-hosted provider.
func NewDefaultProvider(name string) Provider {
Expand Down
2 changes: 1 addition & 1 deletion configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
if providerReq, exists := c.Module.ProviderRequirements[addr.LocalName]; exists {
provider = providerReq.Type
} else {
provider = addrs.NewDefaultProvider(addr.LocalName)
provider = addrs.ImpliedProviderForUnqualifiedType(addr.LocalName)
}

return addrs.AbsProviderConfig{
Expand Down
2 changes: 2 additions & 0 deletions configs/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func TestConfigProviderRequirements(t *testing.T) {
nullProvider := addrs.NewDefaultProvider("null")
randomProvider := addrs.NewDefaultProvider("random")
impliedProvider := addrs.NewDefaultProvider("implied")
terraformProvider := addrs.NewBuiltInProvider("terraform")

got, diags := cfg.ProviderRequirements()
assertNoDiagnostics(t, diags)
Expand All @@ -134,6 +135,7 @@ func TestConfigProviderRequirements(t *testing.T) {
tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"),
impliedProvider: nil,
happycloudProvider: nil,
terraformProvider: nil,
}

if diff := cmp.Diff(want, got); diff != "" {
Expand Down
13 changes: 6 additions & 7 deletions configs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
}
diags = append(diags, hclDiags...)
} else {
fqn = addrs.NewDefaultProvider(reqd.Name)
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
}
if existing, exists := m.ProviderRequirements[reqd.Name]; exists {
if existing.Type != fqn {
Expand Down Expand Up @@ -286,11 +286,11 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
r.Provider = existing.Type
} else {
r.Provider = addrs.NewDefaultProvider(r.ProviderConfigAddr().LocalName)
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
}
continue
}
r.Provider = addrs.NewDefaultProvider(r.Addr().ImpliedProvider())
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.Addr().ImpliedProvider())
}

for _, r := range file.DataResources {
Expand All @@ -310,13 +310,12 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
if r.ProviderConfigRef != nil {
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
r.Provider = existing.Type

} else {
r.Provider = addrs.NewDefaultProvider(r.ProviderConfigAddr().LocalName)
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
}
continue
}
r.Provider = addrs.NewDefaultProvider(r.Addr().ImpliedProvider())
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.Addr().ImpliedProvider())
}

return diags
Expand Down Expand Up @@ -511,5 +510,5 @@ func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Prov
if provider, exists := m.ProviderRequirements[pc.LocalName]; exists {
return provider.Type
}
return addrs.NewDefaultProvider(pc.LocalName)
return addrs.ImpliedProviderForUnqualifiedType(pc.LocalName)
}
4 changes: 2 additions & 2 deletions configs/module_merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func mergeProviderVersionConstraints(recv map[string]ProviderRequirements, ovrd
// any errors parsing the source string will have already been captured.
fqn, _ = addrs.ParseProviderSourceString(reqd.Source.SourceStr)
} else {
fqn = addrs.NewDefaultProvider(reqd.Name)
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
}
recv[reqd.Name] = ProviderRequirements{Type: fqn, VersionConstraints: []VersionConstraint{reqd.Requirement}}
}
Expand Down Expand Up @@ -218,7 +218,7 @@ func (r *Resource) merge(or *Resource, prs map[string]ProviderRequirements) hcl.
if existing, exists := prs[or.ProviderConfigRef.Name]; exists {
r.Provider = existing.Type
} else {
r.Provider = addrs.NewDefaultProvider(r.ProviderConfigRef.Name)
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigRef.Name)
}
}

Expand Down
12 changes: 12 additions & 0 deletions configs/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ func TestNewModule_provider_local_name(t *testing.T) {
if localName != "nonexist" {
t.Error("wrong local name returned for a non-local provider")
}

// can also look up the "terraform" provider and see that it sources is
// allowed to be overridden, even though there is a builtin provider
// called "terraform".
p = addrs.NewProvider(addrs.DefaultRegistryHost, "not-builtin", "not-terraform")
if name, exists := mod.ProviderLocalNames[p]; !exists {
t.Fatal("provider FQN not-builtin/not-terraform not found")
} else {
if name != "terraform" {
t.Fatalf("provider localname mismatch: got %s, want terraform", name)
}
}
}

// This test validates the provider FQNs set in each Resource
Expand Down
7 changes: 7 additions & 0 deletions configs/testdata/provider-reqs/provider-reqs-root.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ resource "implied_foo" "bar" {
module "child" {
source = "./child"
}

# There is no provider in required_providers called "terraform", but for
# this name in particular we imply terraform.io/builtin/terraform instead,
# to avoid selecting the now-unmaintained
# registry.terraform.io/hashicorp/terraform.
data "terraform_remote_state" "bar" {
}
3 changes: 3 additions & 0 deletions configs/testdata/providers-explicit-fqn/root.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ terraform {
foo-test = {
source = "foo/test"
}
terraform = {
source = "not-builtin/not-terraform"
}
}
}
2 changes: 1 addition & 1 deletion internal/earlyconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (c *Config) addProviderRequirements(reqs getproviders.Requirements) tfdiags
fqn = addr
}
if fqn.IsZero() {
fqn = addrs.NewDefaultProvider(localName)
fqn = addrs.ImpliedProviderForUnqualifiedType(localName)
}
if _, ok := reqs[fqn]; !ok {
// We'll at least have an unconstrained dependency then, but might
Expand Down
2 changes: 1 addition & 1 deletion terraform/node_resource_abstract.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func (n *NodeAbstractResource) Provider() addrs.Provider {
if n.Config != nil {
return n.Config.Provider
}
return addrs.NewDefaultProvider(n.Addr.Resource.ImpliedProvider())
return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ImpliedProvider())
}

// GraphNodeProviderConsumer
Expand Down

0 comments on commit 7caf0b9

Please sign in to comment.