mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-19 02:39:17 -05:00
* stacks: refactor promises so they declare id when starting * stacks: add some missing perEval promises * formatting
229 lines
6.9 KiB
Go
229 lines
6.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package stackaddrs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/collections"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
// Component is the address of a "component" block within a stack config.
|
|
type Component struct {
|
|
Name string
|
|
}
|
|
|
|
func (Component) referenceableSigil() {}
|
|
func (Component) inStackConfigSigil() {}
|
|
func (Component) inStackInstanceSigil() {}
|
|
|
|
func (c Component) String() string {
|
|
return "component." + c.Name
|
|
}
|
|
|
|
func (c Component) UniqueKey() collections.UniqueKey[Component] {
|
|
return c
|
|
}
|
|
|
|
// A Component is its own [collections.UniqueKey].
|
|
func (Component) IsUniqueKey(Component) {}
|
|
|
|
// ConfigComponent places a [Component] in the context of a particular [Stack].
|
|
type ConfigComponent = InStackConfig[Component]
|
|
|
|
// AbsComponent places a [Component] in the context of a particular [StackInstance].
|
|
type AbsComponent = InStackInstance[Component]
|
|
|
|
func AbsComponentToInstance(ist AbsComponent, ik addrs.InstanceKey) AbsComponentInstance {
|
|
return AbsComponentInstance{
|
|
Stack: ist.Stack,
|
|
Item: ComponentInstance{
|
|
Component: ist.Item,
|
|
Key: ik,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ComponentInstance is the address of a dynamic instance of a component.
|
|
type ComponentInstance struct {
|
|
Component Component
|
|
Key addrs.InstanceKey
|
|
}
|
|
|
|
func (ComponentInstance) inStackConfigSigil() {}
|
|
func (ComponentInstance) inStackInstanceSigil() {}
|
|
|
|
func (c ComponentInstance) String() string {
|
|
if c.Key == nil {
|
|
return c.Component.String()
|
|
}
|
|
return c.Component.String() + c.Key.String()
|
|
}
|
|
|
|
func (c ComponentInstance) UniqueKey() collections.UniqueKey[ComponentInstance] {
|
|
return c
|
|
}
|
|
|
|
// A ComponentInstance is its own [collections.UniqueKey].
|
|
func (ComponentInstance) IsUniqueKey(ComponentInstance) {}
|
|
|
|
// ConfigComponentInstance places a [ComponentInstance] in the context of a
|
|
// particular [Stack].
|
|
type ConfigComponentInstance = InStackConfig[ComponentInstance]
|
|
|
|
// AbsComponentInstance places a [ComponentInstance] in the context of a
|
|
// particular [StackInstance].
|
|
type AbsComponentInstance = InStackInstance[ComponentInstance]
|
|
|
|
func ConfigComponentForAbsInstance(instAddr AbsComponentInstance) ConfigComponent {
|
|
configInst := ConfigForAbs(instAddr) // a ConfigComponentInstance
|
|
return ConfigComponent{
|
|
Stack: configInst.Stack,
|
|
Item: Component{
|
|
Name: configInst.Item.Component.Name,
|
|
},
|
|
}
|
|
}
|
|
|
|
func ParseAbsComponentInstance(traversal hcl.Traversal) (AbsComponentInstance, tfdiags.Diagnostics) {
|
|
inst, remain, diags := ParseAbsComponentInstanceOnly(traversal)
|
|
if diags.HasErrors() {
|
|
return AbsComponentInstance{}, diags
|
|
}
|
|
|
|
if len(remain) > 0 {
|
|
// Then we have some remaining traversal steps that weren't consumed
|
|
// by the component instance address itself, which is an error when the
|
|
// caller is using this function.
|
|
rng := remain.SourceRange()
|
|
// if "remain" is empty then the source range would be zero length,
|
|
// and so we'll use the original traversal instead.
|
|
if len(remain) == 0 {
|
|
rng = traversal.SourceRange()
|
|
}
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid component instance address",
|
|
Detail: "The component instance address must include the keyword \"component\" followed by a component name.",
|
|
Subject: &rng,
|
|
})
|
|
return AbsComponentInstance{}, diags
|
|
}
|
|
|
|
return inst, diags
|
|
}
|
|
|
|
func ParseAbsComponentInstanceStr(s string) (AbsComponentInstance, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(s), "", hcl.InitialPos)
|
|
diags = diags.Append(hclDiags)
|
|
if diags.HasErrors() {
|
|
return AbsComponentInstance{}, diags
|
|
}
|
|
|
|
ret, moreDiags := ParseAbsComponentInstance(traversal)
|
|
diags = diags.Append(moreDiags)
|
|
return ret, diags
|
|
}
|
|
|
|
func ParsePartialComponentInstanceStr(s string) (AbsComponentInstance, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
traversal, hclDiags := hclsyntax.ParseTraversalPartial([]byte(s), "", hcl.InitialPos)
|
|
diags = diags.Append(hclDiags)
|
|
if diags.HasErrors() {
|
|
return AbsComponentInstance{}, diags
|
|
}
|
|
|
|
ret, moreDiags := ParseAbsComponentInstance(traversal)
|
|
diags = diags.Append(moreDiags)
|
|
return ret, diags
|
|
}
|
|
|
|
func ParseAbsComponentInstanceStrOnly(s string) (AbsComponentInstance, hcl.Traversal, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
traversal, hclDiags := hclsyntax.ParseTraversalPartial([]byte(s), "", hcl.InitialPos)
|
|
diags = diags.Append(hclDiags)
|
|
if diags.HasErrors() {
|
|
return AbsComponentInstance{}, traversal, diags
|
|
}
|
|
|
|
ret, rest, moreDiags := ParseAbsComponentInstanceOnly(traversal)
|
|
diags = diags.Append(moreDiags)
|
|
return ret, rest, diags
|
|
}
|
|
|
|
func ParseAbsComponentInstanceOnly(traversal hcl.Traversal) (AbsComponentInstance, hcl.Traversal, tfdiags.Diagnostics) {
|
|
if traversal.IsRelative() {
|
|
// This is always a caller bug: caller must only pass absolute
|
|
// traversals in here.
|
|
panic("ParseAbsComponentInstanceOnly with relative traversal")
|
|
}
|
|
|
|
stackInst, remain, diags := parseInStackInstancePrefix(traversal)
|
|
if diags.HasErrors() {
|
|
return AbsComponentInstance{}, remain, diags
|
|
}
|
|
|
|
// "remain" should now be the keyword "component" followed by a valid
|
|
// component name, optionally followed by an instance key.
|
|
const diagSummary = "Invalid component instance address"
|
|
|
|
if kwStep, ok := remain[0].(hcl.TraverseAttr); !ok || kwStep.Name != "component" {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: diagSummary,
|
|
Detail: "The component instance address must include the keyword \"component\" followed by a component name.",
|
|
Subject: remain[0].SourceRange().Ptr(),
|
|
})
|
|
return AbsComponentInstance{}, remain, diags
|
|
}
|
|
remain = remain[1:]
|
|
|
|
nameStep, ok := remain[0].(hcl.TraverseAttr)
|
|
if !ok {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: diagSummary,
|
|
Detail: "The component instance address must include the keyword \"component\" followed by a component name.",
|
|
Subject: remain[1].SourceRange().Ptr(),
|
|
})
|
|
return AbsComponentInstance{}, remain, diags
|
|
}
|
|
remain = remain[1:]
|
|
componentAddr := ComponentInstance{
|
|
Component: Component{Name: nameStep.Name},
|
|
}
|
|
|
|
if len(remain) > 0 {
|
|
switch instStep := remain[0].(type) {
|
|
case hcl.TraverseIndex:
|
|
var err error
|
|
componentAddr.Key, err = addrs.ParseInstanceKey(instStep.Key)
|
|
if err != nil {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: diagSummary,
|
|
Detail: fmt.Sprintf("Invalid instance key: %s.", err),
|
|
Subject: instStep.SourceRange().Ptr(),
|
|
})
|
|
return AbsComponentInstance{}, remain, diags
|
|
}
|
|
|
|
remain = remain[1:]
|
|
case hcl.TraverseSplat:
|
|
componentAddr.Key = addrs.WildcardKey
|
|
remain = remain[1:]
|
|
}
|
|
}
|
|
|
|
return AbsComponentInstance{
|
|
Stack: stackInst,
|
|
Item: componentAddr,
|
|
}, remain, diags
|
|
}
|