You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraform/internal/stacks/stackconfig/declarations.go

261 lines
6.9 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package stackconfig
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// Declarations represent the various items that can be declared in a stack
// configuration.
//
// This just represents the fields that both [Stack] and [File] have in common,
// so we can share some code between the two.
type Declarations struct {
// EmbeddedStacks are calls to other stack configurations that should
// be treated as a part of the overall desired state produced from this
// stack. These are declared with "stack" blocks in the stack language.
EmbeddedStacks map[string]*EmbeddedStack
// Components are calls to trees of Terraform modules that represent the
// real infrastructure described by a stack.
Components map[string]*Component
// InputVariables, LocalValues, and OutputValues together represent all
// of the "named values" in the stack configuration, which are just glue
// to pass values between scopes or to factor out common expressions for
// reuse in multiple locations.
InputVariables map[string]*InputVariable
LocalValues map[string]*LocalValue
OutputValues map[string]*OutputValue
// RequiredProviders represents the single required_providers block
// that's allowed in any stack, declaring which providers this stack
// depends on and which versions of those providers it is compatible with.
RequiredProviders *ProviderRequirements
// ProviderConfigs are the provider configurations declared in this
// particular stack configuration. Other stack configurations in the
// overall tree might have their own provider configurations.
ProviderConfigs map[addrs.LocalProviderConfig]*ProviderConfig
}
func makeDeclarations() Declarations {
return Declarations{
EmbeddedStacks: make(map[string]*EmbeddedStack),
Components: make(map[string]*Component),
InputVariables: make(map[string]*InputVariable),
LocalValues: make(map[string]*LocalValue),
OutputValues: make(map[string]*OutputValue),
ProviderConfigs: make(map[addrs.LocalProviderConfig]*ProviderConfig),
}
}
func (d *Declarations) addComponent(decl *Component) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
name := decl.Name
if existing, exists := d.Components[name]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate component declaration",
Detail: fmt.Sprintf(
"An component named %q was already declared at %s.",
name, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.Components[name] = decl
return diags
}
func (d *Declarations) addEmbeddedStack(decl *EmbeddedStack) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
name := decl.Name
if existing, exists := d.EmbeddedStacks[name]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate embedded stack call",
Detail: fmt.Sprintf(
"An embedded stack call named %q was already declared at %s.",
name, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.EmbeddedStacks[name] = decl
return diags
}
func (d *Declarations) addInputVariable(decl *InputVariable) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
name := decl.Name
if existing, exists := d.InputVariables[name]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate input variable declaration",
Detail: fmt.Sprintf(
"An input variable named %q was already declared at %s.",
name, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.InputVariables[name] = decl
return diags
}
func (d *Declarations) addLocalValue(decl *LocalValue) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
name := decl.Name
if existing, exists := d.LocalValues[name]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate local value declaration",
Detail: fmt.Sprintf(
"A local value named %q was already declared at %s.",
name, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.LocalValues[name] = decl
return diags
}
func (d *Declarations) addOutputValue(decl *OutputValue) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
name := decl.Name
if existing, exists := d.OutputValues[name]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate output value declaration",
Detail: fmt.Sprintf(
"An output value named %q was already declared at %s.",
name, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.OutputValues[name] = decl
return diags
}
func (d *Declarations) addRequiredProviders(decl *ProviderRequirements) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
if d.RequiredProviders != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider requirements",
Detail: fmt.Sprintf(
"This stack's provider requirements were already declared at %s.",
d.RequiredProviders.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.RequiredProviders = decl
return diags
}
func (d *Declarations) addProviderConfig(decl *ProviderConfig) tfdiags.Diagnostics {
if decl == nil {
return nil
}
var diags tfdiags.Diagnostics
addr := decl.LocalAddr
if existing, exists := d.ProviderConfigs[addr]; exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider configuration",
Detail: fmt.Sprintf(
"An configuration named %q for provider %q was already declared at %s.",
addr.LocalName, addr.Alias, existing.DeclRange.ToHCL(),
),
Subject: decl.DeclRange.ToHCL().Ptr(),
})
return diags
}
d.ProviderConfigs[addr] = decl
return diags
}
func (d *Declarations) merge(other *Declarations) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
for _, decl := range other.EmbeddedStacks {
diags = diags.Append(
d.addEmbeddedStack(decl),
)
}
for _, decl := range other.Components {
diags = diags.Append(
d.addComponent(decl),
)
}
for _, decl := range other.InputVariables {
diags = diags.Append(
d.addInputVariable(decl),
)
}
for _, decl := range other.LocalValues {
diags = diags.Append(
d.addLocalValue(decl),
)
}
for _, decl := range other.OutputValues {
diags = diags.Append(
d.addOutputValue(decl),
)
}
if other.RequiredProviders != nil {
d.addRequiredProviders(other.RequiredProviders)
}
for _, decl := range other.ProviderConfigs {
diags = diags.Append(
d.addProviderConfig(decl),
)
}
return diags
}