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

stacks: provide stack and config on component creation #36778

Merged
merged 1 commit into from
Apr 1, 2025
Merged
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
55 changes: 15 additions & 40 deletions internal/stacks/stackruntime/internal/stackeval/component.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@ import (
"github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/promising"
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
"github.com/hashicorp/terraform/internal/stacks/stackconfig"
"github.com/hashicorp/terraform/internal/stacks/stackplan"
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
"github.com/hashicorp/terraform/internal/stacks/stackstate"
@@ -24,7 +23,9 @@ import (
type Component struct {
addr stackaddrs.AbsComponent

main *Main
main *Main
stack *Stack
config *ComponentConfig

forEachValue perEvalPhase[promising.Once[withDiagnostics[cty.Value]]]
instances perEvalPhase[promising.Once[withDiagnostics[instancesResult[*ComponentInstance]]]]
@@ -35,41 +36,15 @@ var _ Plannable = (*Component)(nil)
var _ Applyable = (*Component)(nil)
var _ Referenceable = (*Component)(nil)

func newComponent(main *Main, addr stackaddrs.AbsComponent) *Component {
func newComponent(main *Main, addr stackaddrs.AbsComponent, stack *Stack, config *ComponentConfig) *Component {
return &Component{
addr: addr,
main: main,
addr: addr,
main: main,
stack: stack,
config: config,
}
}

func (c *Component) Addr() stackaddrs.AbsComponent {
return c.addr
}

func (c *Component) Config() *ComponentConfig {
configAddr := stackaddrs.ConfigForAbs(c.Addr())
stackConfig := c.main.StackConfig(configAddr.Stack)
if stackConfig == nil {
return nil
}
return stackConfig.Component(configAddr.Item)
}

func (c *Component) Declaration() *stackconfig.Component {
cfg := c.Config()
if cfg == nil {
return nil
}
return cfg.Declaration()
}

func (c *Component) Stack() *Stack {
// Unchecked because we should've been constructed from the same stack
// object we're about to return, and so this should be valid unless
// the original construction was from an invalid object itself.
return c.main.StackUnchecked(c.Addr().Stack)
}

// ForEachValue returns the result of evaluating the "for_each" expression
// for this stack call, with the following exceptions:
// - If the stack call doesn't use "for_each" at all, returns [cty.NilVal].
@@ -107,12 +82,12 @@ func (c *Component) CheckForEachValue(ctx context.Context, phase EvalPhase) (cty
ctx, c.tracingName()+" for_each", c.forEachValue.For(phase),
func(ctx context.Context) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
cfg := c.Declaration()
cfg := c.config.config

switch {

case cfg.ForEach != nil:
result, moreDiags := evaluateForEachExpr(ctx, cfg.ForEach, phase, c.Stack(), "component")
result, moreDiags := evaluateForEachExpr(ctx, cfg.ForEach, phase, c.stack, "component")
diags = diags.Append(moreDiags)
if diags.HasErrors() {
return cty.DynamicVal, diags
@@ -179,7 +154,7 @@ func (c *Component) CheckInstances(ctx context.Context, phase EvalPhase) (map[ad

h := hooksFromContext(ctx)
hookSingle(ctx, h.ComponentExpanded, &hooks.ComponentInstances{
ComponentAddr: c.Addr(),
ComponentAddr: c.addr,
InstanceAddrs: addrs,
})

@@ -202,7 +177,7 @@ func (c *Component) UnknownInstance(ctx context.Context, phase EvalPhase) *Compo
}

func (c *Component) ResultValue(ctx context.Context, phase EvalPhase) cty.Value {
decl := c.Declaration()
decl := c.config.config
insts, unknown := c.Instances(ctx, phase)

switch {
@@ -326,15 +301,15 @@ func (c *Component) PlanChanges(ctx context.Context) ([]stackplan.PlannedChange,

// References implements Referrer
func (c *Component) References(context.Context) []stackaddrs.AbsReference {
cfg := c.Declaration()
cfg := c.config.config
var ret []stackaddrs.Reference
ret = append(ret, ReferencesInExpr(cfg.ForEach)...)
ret = append(ret, ReferencesInExpr(cfg.Inputs)...)
for _, expr := range cfg.ProviderConfigs {
ret = append(ret, ReferencesInExpr(expr)...)
}
ret = append(ret, referencesInTraversals(cfg.DependsOn)...)
return makeReferencesAbsolute(ret, c.Addr().Stack)
return makeReferencesAbsolute(ret, c.addr.Stack)
}

// RequiredComponents returns the set of required components for this component.
@@ -372,5 +347,5 @@ func (c *Component) ApplySuccessful(ctx context.Context) bool {
}

func (c *Component) tracingName() string {
return c.Addr().String()
return c.addr.String()
}
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ var (

type ComponentConfig struct {
addr stackaddrs.ConfigComponent
stack *StackConfig
config *stackconfig.Component

main *Main
@@ -44,28 +45,28 @@ type ComponentConfig struct {
moduleTree promising.Once[withDiagnostics[*configs.Config]] // moduleTree is constant across all phases
}

func newComponentConfig(main *Main, addr stackaddrs.ConfigComponent, config *stackconfig.Component) *ComponentConfig {
func newComponentConfig(main *Main, addr stackaddrs.ConfigComponent, stack *StackConfig, config *stackconfig.Component) *ComponentConfig {
return &ComponentConfig{
addr: addr,
stack: stack,
config: config,
main: main,
}
}

// Addr implements ConfigComponentExpressionScope
func (c *ComponentConfig) Addr() stackaddrs.ConfigComponent {
return c.addr
}

func (c *ComponentConfig) Declaration() *stackconfig.Component {
return c.config
}

// DeclRange implements ConfigComponentExpressionScope
func (c *ComponentConfig) DeclRange() *hcl.Range {
return c.config.DeclRange.ToHCL().Ptr()
}

// StackConfig implements ConfigComponentExpressionScope
func (c *ComponentConfig) StackConfig() *StackConfig {
return c.main.mustStackConfig(c.addr.Stack)
return c.stack
}

// ModuleTree returns the static representation of the tree of modules starting
@@ -88,10 +89,9 @@ func (c *ComponentConfig) CheckModuleTree(ctx context.Context) (*configs.Config,
func(ctx context.Context) (*configs.Config, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

decl := c.Declaration()
sources := c.main.SourceBundle()

rootModuleSource := decl.FinalSourceAddr
rootModuleSource := c.config.FinalSourceAddr
if rootModuleSource == nil {
// If we get here then the configuration was loaded incorrectly,
// either by the stackconfig package or by the caller of the
@@ -107,7 +107,7 @@ func (c *ComponentConfig) CheckModuleTree(ctx context.Context) (*configs.Config,
Severity: hcl.DiagError,
Summary: "Can't load module for component",
Detail: fmt.Sprintf("The source location %s does not contain a Terraform module.", rootModuleSource),
Subject: decl.SourceAddrRange.ToHCL().Ptr(),
Subject: c.config.SourceAddrRange.ToHCL().Ptr(),
})
return nil, diags
}
@@ -239,11 +239,10 @@ func (c *ComponentConfig) CheckInputVariableValues(ctx context.Context, phase Ev
return nil
}

decl := c.Declaration()
varDecls := c.RootModuleVariableDecls(ctx)

// We don't care about the returned value, only that it has no errors.
_, diags := EvalComponentInputVariables(ctx, varDecls, wantTy, defs, decl, phase, c)
_, diags := EvalComponentInputVariables(ctx, varDecls, wantTy, defs, c.config, phase, c)
return diags
}

@@ -266,17 +265,17 @@ func (c *ComponentConfig) ExprReferenceValue(ctx context.Context, phase EvalPhas
// ResolveExpressionReference implements ExpressionScope.
func (c *ComponentConfig) ResolveExpressionReference(ctx context.Context, ref stackaddrs.Reference) (Referenceable, tfdiags.Diagnostics) {
repetition := instances.RepetitionData{}
if c.Declaration().ForEach != nil {
if c.config.ForEach != nil {
// For validation, we'll return unknown for the instance data.
repetition.EachKey = cty.UnknownVal(cty.String).RefineNotNull()
repetition.EachValue = cty.DynamicVal
}
return c.StackConfig().resolveExpressionReference(ctx, ref, nil, repetition)
return c.stack.resolveExpressionReference(ctx, ref, nil, repetition)
}

// ExternalFunctions implements ExpressionScope.
func (c *ComponentConfig) ExternalFunctions(ctx context.Context) (lang.ExternalFuncs, tfdiags.Diagnostics) {
return c.main.ProviderFunctions(ctx, c.StackConfig())
return c.main.ProviderFunctions(ctx, c.stack)
}

// PlanTimestamp implements ExpressionScope, providing the timestamp at which
@@ -294,19 +293,18 @@ func (c *ComponentConfig) checkValid(ctx context.Context, phase EvalPhase) tfdia
if moduleTree == nil {
return diags, nil
}
decl := c.Declaration()

variableDiags := c.CheckInputVariableValues(ctx, phase)
diags = diags.Append(variableDiags)

dependsOnDiags := ValidateDependsOn(c.StackConfig(), c.config.DependsOn)
dependsOnDiags := ValidateDependsOn(c.stack, c.config.DependsOn)
diags = diags.Append(dependsOnDiags)

// We don't actually exit if we found errors with the input variables
// or depends_on attribute, we can still validate the actual module tree
// without them.

providerTypes, providerDiags := EvalProviderTypes(ctx, c.StackConfig(), c.config.ProviderConfigs, phase, c)
providerTypes, providerDiags := EvalProviderTypes(ctx, c.stack, c.config.ProviderConfigs, phase, c)
diags = diags.Append(providerDiags)
if providerDiags.HasErrors() {
// If there's invalid provider configuration, we can't actually go
@@ -360,8 +358,8 @@ func (c *ComponentConfig) checkValid(ctx context.Context, phase EvalPhase) tfdia
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot validate component",
Detail: fmt.Sprintf("Cannot validate %s because its provider configuration assignments are invalid.", c.Addr()),
Subject: decl.DeclRange.ToHCL().Ptr(),
Detail: fmt.Sprintf("Cannot validate %s because its provider configuration assignments are invalid.", c.addr),
Subject: c.config.DeclRange.ToHCL().Ptr(),
})
return diags, nil
}
@@ -414,5 +412,5 @@ func (c *ComponentConfig) PlanChanges(ctx context.Context) ([]stackplan.PlannedC
}

func (c *ComponentConfig) tracingName() string {
return c.Addr().String()
return c.addr.String()
}
Loading
Loading