Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

configs: Simplify required_providers blocks #24763

Merged
merged 1 commit into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,23 +185,21 @@ func (c *Config) addProviderRequirements(reqs getproviders.Requirements) hcl.Dia
var diags hcl.Diagnostics

// First we'll deal with the requirements directly in _our_ module...
for _, providerReqs := range c.Module.ProviderRequirements {
for _, providerReqs := range c.Module.ProviderRequirements.RequiredProviders {
fqn := providerReqs.Type
if _, ok := reqs[fqn]; !ok {
// We'll at least have an unconstrained dependency then, but might
// add to this in the loop below.
reqs[fqn] = nil
}
for _, constraintsSrc := range providerReqs.VersionConstraints {
// The model of version constraints in this package is still the
// old one using a different upstream module to represent versions,
// so we'll need to shim that out here for now. We assume this
// will always succeed because these constraints already succeeded
// parsing with the other constraint parser, which uses the same
// syntax.
constraints := getproviders.MustParseVersionConstraints(constraintsSrc.Required.String())
reqs[fqn] = append(reqs[fqn], constraints...)
}
// The model of version constraints in this package is still the
// old one using a different upstream module to represent versions,
// so we'll need to shim that out here for now. We assume this
// will always succeed because these constraints already succeeded
// parsing with the other constraint parser, which uses the same
// syntax.
constraints := getproviders.MustParseVersionConstraints(providerReqs.Requirement.Required.String())
reqs[fqn] = append(reqs[fqn], constraints...)
}
// Each resource in the configuration creates an *implicit* provider
// dependency, though we'll only record it if there isn't already
Expand Down Expand Up @@ -321,7 +319,7 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
}

var provider addrs.Provider
if providerReq, exists := c.Module.ProviderRequirements[addr.LocalName]; exists {
if providerReq, exists := c.Module.ProviderRequirements.RequiredProviders[addr.LocalName]; exists {
provider = providerReq.Type
} else {
provider = addrs.ImpliedProviderForUnqualifiedType(addr.LocalName)
Expand All @@ -343,7 +341,7 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
// by checking for the provider in module.ProviderRequirements and falling
// back to addrs.NewDefaultProvider if it is not found.
func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider {
if provider, exists := c.Module.ProviderRequirements[addr.LocalName]; exists {
if provider, exists := c.Module.ProviderRequirements.RequiredProviders[addr.LocalName]; exists {
return provider.Type
}
return c.ResolveAbsProviderAddr(addr, addrs.RootModule).Provider
Expand Down
104 changes: 52 additions & 52 deletions configs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/experiments"
"github.com/hashicorp/terraform/tfdiags"
)

// Module is a container for a set of configuration constructs that are
Expand All @@ -31,7 +30,7 @@ type Module struct {

Backend *Backend
ProviderConfigs map[string]*Provider
ProviderRequirements map[string]ProviderRequirements
ProviderRequirements *RequiredProviders
ProviderLocalNames map[addrs.Provider]string
ProviderMetas map[addrs.Provider]*ProviderMeta

Expand Down Expand Up @@ -64,7 +63,7 @@ type File struct {
Backends []*Backend
ProviderConfigs []*Provider
ProviderMetas []*ProviderMeta
RequiredProviders []*RequiredProvider
RequiredProviders []*RequiredProviders

Variables []*Variable
Locals []*Local
Expand All @@ -87,16 +86,50 @@ type File struct {
func NewModule(primaryFiles, overrideFiles []*File) (*Module, hcl.Diagnostics) {
var diags hcl.Diagnostics
mod := &Module{
ProviderConfigs: map[string]*Provider{},
ProviderRequirements: map[string]ProviderRequirements{},
ProviderLocalNames: map[addrs.Provider]string{},
Variables: map[string]*Variable{},
Locals: map[string]*Local{},
Outputs: map[string]*Output{},
ModuleCalls: map[string]*ModuleCall{},
ManagedResources: map[string]*Resource{},
DataResources: map[string]*Resource{},
ProviderMetas: map[addrs.Provider]*ProviderMeta{},
ProviderConfigs: map[string]*Provider{},
ProviderLocalNames: map[addrs.Provider]string{},
Variables: map[string]*Variable{},
Locals: map[string]*Local{},
Outputs: map[string]*Output{},
ModuleCalls: map[string]*ModuleCall{},
ManagedResources: map[string]*Resource{},
DataResources: map[string]*Resource{},
ProviderMetas: map[addrs.Provider]*ProviderMeta{},
}

// Process the required_providers blocks first, to ensure that all
// resources have access to the correct provider FQNs
for _, file := range primaryFiles {
for _, r := range file.RequiredProviders {
if mod.ProviderRequirements != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate required providers configuration",
Detail: fmt.Sprintf("A module may have only one required providers configuration. The required providers were previously configured at %s.", mod.ProviderRequirements.DeclRange),
Subject: &r.DeclRange,
})
continue
}
mod.ProviderRequirements = r
}
}

// If no required_providers block is configured, create a useful empty
// state to reduce nil checks elsewhere
if mod.ProviderRequirements == nil {
mod.ProviderRequirements = &RequiredProviders{
RequiredProviders: make(map[string]*RequiredProvider),
}
}

// Any required_providers blocks in override files replace the entire
// block for each provider
for _, file := range overrideFiles {
for _, override := range file.RequiredProviders {
for name, rp := range override.RequiredProviders {
mod.ProviderRequirements.RequiredProviders[name] = rp
}
}
}

for _, file := range primaryFiles {
Expand Down Expand Up @@ -178,35 +211,6 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
m.ProviderConfigs[key] = pc
}

for _, reqd := range file.RequiredProviders {
var fqn addrs.Provider
if reqd.Source.SourceStr != "" {
var sourceDiags tfdiags.Diagnostics
fqn, sourceDiags = addrs.ParseProviderSourceString(reqd.Source.SourceStr)
hclDiags := sourceDiags.ToHCL()
// The diagnostics from ParseProviderSourceString don't contain
// source location information because it has no context to compute
// them from, and so we'll add those in quickly here before we
// return.
for _, diag := range hclDiags {
if diag.Subject == nil {
diag.Subject = reqd.Source.DeclRange.Ptr()
}
}
diags = append(diags, hclDiags...)
} else {
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
}
if existing, exists := m.ProviderRequirements[reqd.Name]; exists {
if existing.Type != fqn {
panic("provider fqn mismatch")
}
existing.VersionConstraints = append(existing.VersionConstraints, reqd.Requirement)
} else {
m.ProviderRequirements[reqd.Name] = ProviderRequirements{Type: fqn, VersionConstraints: []VersionConstraint{reqd.Requirement}}
}
}

for _, pm := range file.ProviderMetas {
provider := m.ProviderForLocalConfig(addrs.LocalProviderConfig{LocalName: pm.Provider})
if existing, exists := m.ProviderMetas[provider]; exists {
Expand Down Expand Up @@ -283,7 +287,7 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {

// set the provider FQN for the resource
if r.ProviderConfigRef != nil {
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
if existing, exists := m.ProviderRequirements.RequiredProviders[r.ProviderConfigAddr().LocalName]; exists {
r.Provider = existing.Type
} else {
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
Expand All @@ -308,7 +312,7 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {

// set the provider FQN for the resource
if r.ProviderConfigRef != nil {
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
if existing, exists := m.ProviderRequirements.RequiredProviders[r.ProviderConfigAddr().LocalName]; exists {
r.Provider = existing.Type
} else {
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
Expand Down Expand Up @@ -382,10 +386,6 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
}
}

if len(file.RequiredProviders) != 0 {
mergeProviderVersionConstraints(m.ProviderRequirements, file.RequiredProviders)
}

for _, v := range file.Variables {
existing, exists := m.Variables[v.Name]
if !exists {
Expand Down Expand Up @@ -458,7 +458,7 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
})
continue
}
mergeDiags := existing.merge(r, m.ProviderRequirements)
mergeDiags := existing.merge(r, m.ProviderRequirements.RequiredProviders)
diags = append(diags, mergeDiags...)
}

Expand All @@ -474,7 +474,7 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
})
continue
}
mergeDiags := existing.merge(r, m.ProviderRequirements)
mergeDiags := existing.merge(r, m.ProviderRequirements.RequiredProviders)
diags = append(diags, mergeDiags...)
}

Expand All @@ -487,7 +487,7 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
// only be populated after the module has been parsed.
func (m *Module) gatherProviderLocalNames() {
providers := make(map[addrs.Provider]string)
for k, v := range m.ProviderRequirements {
for k, v := range m.ProviderRequirements.RequiredProviders {
providers[v.Type] = k
}
m.ProviderLocalNames = providers
Expand All @@ -507,7 +507,7 @@ func (m *Module) LocalNameForProvider(p addrs.Provider) string {

// ProviderForLocalConfig returns the provider FQN for a given LocalProviderConfig
func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Provider {
if provider, exists := m.ProviderRequirements[pc.LocalName]; exists {
if provider, exists := m.ProviderRequirements.RequiredProviders[pc.LocalName]; exists {
return provider.Type
}
return addrs.ImpliedProviderForUnqualifiedType(pc.LocalName)
Expand Down
23 changes: 2 additions & 21 deletions configs/module_merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,6 @@ func (p *Provider) merge(op *Provider) hcl.Diagnostics {
return diags
}

func mergeProviderVersionConstraints(recv map[string]ProviderRequirements, ovrd []*RequiredProvider) {
// Any provider name that's mentioned in the override gets nilled out in
// our map so that we'll rebuild it below. Any provider not mentioned is
// left unchanged.
for _, reqd := range ovrd {
delete(recv, reqd.Name)
}
for _, reqd := range ovrd {
var fqn addrs.Provider
if reqd.Source.SourceStr != "" {
// any errors parsing the source string will have already been captured.
fqn, _ = addrs.ParseProviderSourceString(reqd.Source.SourceStr)
} else {
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
}
recv[reqd.Name] = ProviderRequirements{Type: fqn, VersionConstraints: []VersionConstraint{reqd.Requirement}}
}
}

func (v *Variable) merge(ov *Variable) hcl.Diagnostics {
var diags hcl.Diagnostics

Expand Down Expand Up @@ -197,7 +178,7 @@ func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
return diags
}

func (r *Resource) merge(or *Resource, prs map[string]ProviderRequirements) hcl.Diagnostics {
func (r *Resource) merge(or *Resource, rps map[string]*RequiredProvider) hcl.Diagnostics {
var diags hcl.Diagnostics

if r.Mode != or.Mode {
Expand All @@ -215,7 +196,7 @@ func (r *Resource) merge(or *Resource, prs map[string]ProviderRequirements) hcl.

if or.ProviderConfigRef != nil {
r.ProviderConfigRef = or.ProviderConfigRef
if existing, exists := prs[or.ProviderConfigRef.Name]; exists {
if existing, exists := rps[or.ProviderConfigRef.Name]; exists {
r.Provider = existing.Type
} else {
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigRef.Name)
Expand Down
96 changes: 0 additions & 96 deletions configs/module_merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package configs
import (
"testing"

version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/terraform/addrs"
Expand Down Expand Up @@ -232,98 +231,3 @@ func TestModuleOverrideResourceFQNs(t *testing.T) {
t.Fatalf("wrong result: found provider config ref %s, expected nil", got.ProviderConfigRef)
}
}

func TestMergeProviderVersionConstraints(t *testing.T) {
v1, _ := version.NewConstraint("1.0.0")
vc1 := VersionConstraint{
Required: v1,
}
v2, _ := version.NewConstraint("2.0.0")
vc2 := VersionConstraint{
Required: v2,
}

tests := map[string]struct {
Input map[string]ProviderRequirements
Override []*RequiredProvider
Want map[string]ProviderRequirements
}{
"basic merge": {
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.Provider{Type: "random"},
VersionConstraints: []VersionConstraint{},
},
},
[]*RequiredProvider{
&RequiredProvider{
Name: "null",
Requirement: VersionConstraint{},
},
},
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.Provider{Type: "random"},
VersionConstraints: []VersionConstraint{},
},
"null": ProviderRequirements{
Type: addrs.NewDefaultProvider("null"),
VersionConstraints: []VersionConstraint{
VersionConstraint{
Required: version.Constraints(nil),
DeclRange: hcl.Range{},
},
},
},
},
},
"override version constraint": {
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.Provider{Type: "random"},
VersionConstraints: []VersionConstraint{vc1},
},
},
[]*RequiredProvider{
&RequiredProvider{
Name: "random",
Requirement: vc2,
},
},
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.NewDefaultProvider("random"),
VersionConstraints: []VersionConstraint{vc2},
},
},
},
"merge with source constraint": {
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.Provider{Type: "random"},
VersionConstraints: []VersionConstraint{vc1},
},
},
[]*RequiredProvider{
&RequiredProvider{
Name: "random",
Source: Source{SourceStr: "hashicorp/random"},
Requirement: vc2,
},
},
map[string]ProviderRequirements{
"random": ProviderRequirements{
Type: addrs.NewDefaultProvider("random"),
VersionConstraints: []VersionConstraint{vc2},
},
},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
mergeProviderVersionConstraints(test.Input, test.Override)
assertResultDeepEqual(t, test.Input, test.Want)
})
}
}
Loading