Skip to content

Commit 44c41e1

Browse files
committed
stacks: include existing components when deferring nested stacks
1 parent ffa57f4 commit 44c41e1

File tree

12 files changed

+455
-320
lines changed

12 files changed

+455
-320
lines changed

internal/stacks/stackaddrs/in_stack.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/hashicorp/hcl/v2"
1010
"github.com/hashicorp/hcl/v2/hclsyntax"
11+
1112
"github.com/hashicorp/terraform/internal/addrs"
1213
"github.com/hashicorp/terraform/internal/collections"
1314
"github.com/hashicorp/terraform/internal/tfdiags"
@@ -182,8 +183,8 @@ Steps:
182183
}
183184
traversal = traversal[2:] // consume the first two steps that we already dealt with
184185
if len(traversal) > 0 {
185-
idxStep, ok := traversal[0].(hcl.TraverseIndex)
186-
if ok {
186+
switch idxStep := traversal[0].(type) {
187+
case hcl.TraverseIndex:
187188
var err error
188189
addrStep.Key, err = addrs.ParseInstanceKey(idxStep.Key)
189190
if err != nil {
@@ -196,6 +197,9 @@ Steps:
196197
return nil, nil, diags
197198
}
198199
traversal = traversal[1:] // consume the step we just dealt with
200+
case hcl.TraverseSplat:
201+
addrStep.Key = addrs.WildcardKey
202+
traversal = traversal[1:]
199203
}
200204
}
201205
stackInst = append(stackInst, addrStep)

internal/stacks/stackplan/plan.go

+21
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ func (p *Plan) ComponentInstances(addr stackaddrs.AbsComponent) collections.Set[
107107
return ret
108108
}
109109

110+
func (p *Plan) StackInstances(addr stackaddrs.AbsStackCall) map[stackaddrs.StackInstanceStep]bool {
111+
ret := make(map[stackaddrs.StackInstanceStep]bool)
112+
for key := range p.Components.All() {
113+
if len(key.Stack) == 0 {
114+
continue
115+
}
116+
117+
last := key.Stack[len(key.Stack)-1]
118+
path := key.Stack[:len(key.Stack)-1]
119+
120+
if path.String() != addr.Stack.String() {
121+
continue
122+
}
123+
if last.Name != addr.Item.Name {
124+
continue
125+
}
126+
ret[last] = true
127+
}
128+
return ret
129+
}
130+
110131
// RequiredProviderInstances returns a description of all of the provider
111132
// instance slots that are required to satisfy the resource instances
112133
// belonging to the given component instance.

internal/stacks/stackruntime/internal/stackeval/component.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (c *Component) CheckInstances(ctx context.Context, phase EvalPhase) (map[ad
144144
}
145145

146146
result := instancesMap(forEachVal, func(ik addrs.InstanceKey, rd instances.RepetitionData) *ComponentInstance {
147-
return newComponentInstance(c, stackaddrs.AbsComponentToInstance(c.addr, ik), rd, false)
147+
return newComponentInstance(c, stackaddrs.AbsComponentToInstance(c.addr, ik), rd, c.stack.deferred)
148148
})
149149

150150
addrs := make([]stackaddrs.AbsComponentInstance, 0, len(result.insts))

internal/stacks/stackruntime/internal/stackeval/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ func (m *Main) MainStack() *Stack {
310310
defer m.mu.Unlock()
311311

312312
if m.mainStack == nil {
313-
m.mainStack = newStack(m, stackaddrs.RootStackInstance, nil, config, collections.NewMap[stackaddrs.ConfigComponent, []*RemovedComponent]())
313+
m.mainStack = newStack(m, stackaddrs.RootStackInstance, nil, config, collections.NewMap[stackaddrs.ConfigComponent, []*RemovedComponent](), false)
314314
}
315315
return m.mainStack
316316
}

internal/stacks/stackruntime/internal/stackeval/removed_component.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (r *RemovedComponent) Instances(ctx context.Context, phase EvalPhase) (map[
111111
return nil
112112
}
113113

114-
return newRemovedComponentInstance(r, addr, rd, false)
114+
return newRemovedComponentInstance(r, addr, rd, r.stack.deferred)
115115
})
116116

117117
// Now, filter out any instances that are not known to the previous

internal/stacks/stackruntime/internal/stackeval/stack.go

+26-5
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ import (
2929
// repetition arguments (if any) evaluated to determine how many instances
3030
// it has.
3131
type Stack struct {
32-
addr stackaddrs.StackInstance
33-
parent *Stack
34-
config *StackConfig
35-
main *Main
32+
addr stackaddrs.StackInstance
33+
parent *Stack
34+
config *StackConfig
35+
main *Main
36+
deferred bool
3637

3738
// externalRemovedComponents are the set of removed blocks that might
3839
// target components within either this stack or a child of this stack.
@@ -56,11 +57,12 @@ var (
5657
_ Applyable = (*Stack)(nil)
5758
)
5859

59-
func newStack(main *Main, addr stackaddrs.StackInstance, parent *Stack, config *StackConfig, removedComponents collections.Map[stackaddrs.ConfigComponent, []*RemovedComponent]) *Stack {
60+
func newStack(main *Main, addr stackaddrs.StackInstance, parent *Stack, config *StackConfig, removedComponents collections.Map[stackaddrs.ConfigComponent, []*RemovedComponent], deferred bool) *Stack {
6061
return &Stack{
6162
parent: parent,
6263
config: config,
6364
addr: addr,
65+
deferred: deferred,
6466
main: main,
6567
externalRemovedComponents: removedComponents,
6668
}
@@ -201,6 +203,25 @@ func (s *Stack) EmbeddedStackCall(addr stackaddrs.StackCall) *StackCall {
201203
return s.EmbeddedStackCalls()[addr]
202204
}
203205

206+
func (s *Stack) KnownEmbeddedStacks(addr stackaddrs.StackCall, phase EvalPhase) map[stackaddrs.StackInstanceStep]bool {
207+
switch phase {
208+
case PlanPhase:
209+
return s.main.PlanPrevState().StackInstances(stackaddrs.AbsStackCall{
210+
Stack: s.addr,
211+
Item: addr,
212+
})
213+
case ApplyPhase:
214+
return s.main.PlanBeingApplied().StackInstances(stackaddrs.AbsStackCall{
215+
Stack: s.addr,
216+
Item: addr,
217+
})
218+
default:
219+
// We're not executing with an existing state in the other phases, so
220+
// we have no known instances.
221+
return nil
222+
}
223+
}
224+
204225
func (s *Stack) Components() map[stackaddrs.Component]*Component {
205226
s.mu.Lock()
206227
defer s.mu.Unlock()

internal/stacks/stackruntime/internal/stackeval/stack_call.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func (c *StackCall) CheckInstances(ctx context.Context, phase EvalPhase) (map[ad
150150
}
151151

152152
return instancesMap(forEachVal, func(ik addrs.InstanceKey, rd instances.RepetitionData) *StackCallInstance {
153-
return newStackCallInstance(c, ik, rd)
153+
return newStackCallInstance(c, ik, rd, c.stack.deferred)
154154
}), diags
155155
},
156156
)
@@ -159,7 +159,7 @@ func (c *StackCall) CheckInstances(ctx context.Context, phase EvalPhase) (map[ad
159159

160160
func (c *StackCall) UnknownInstance(ctx context.Context, phase EvalPhase) *StackCallInstance {
161161
inst, err := c.unknownInstance.For(phase).Do(ctx, c.tracingName()+" unknown instace", func(ctx context.Context) (*StackCallInstance, error) {
162-
return newStackCallInstance(c, addrs.WildcardKey, instances.UnknownForEachRepetitionData(c.ForEachValue(ctx, phase).Type())), nil
162+
return newStackCallInstance(c, addrs.WildcardKey, instances.UnknownForEachRepetitionData(c.ForEachValue(ctx, phase).Type()), true), nil
163163
})
164164
if err != nil {
165165
// Since we never return an error from the function we pass to Do,

internal/stacks/stackruntime/internal/stackeval/stack_call_instance.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ import (
3131
// are holding a valid [StackCallInstance] then you can call
3232
// [StackCallInstance.CalledStack] to get that stack.
3333
type StackCallInstance struct {
34-
call *StackCall
35-
key addrs.InstanceKey
34+
call *StackCall
35+
key addrs.InstanceKey
36+
deferred bool
3637

3738
main *Main
3839

@@ -45,10 +46,11 @@ type StackCallInstance struct {
4546
var _ ExpressionScope = (*StackCallInstance)(nil)
4647
var _ Plannable = (*StackCallInstance)(nil)
4748

48-
func newStackCallInstance(call *StackCall, key addrs.InstanceKey, repetition instances.RepetitionData) *StackCallInstance {
49+
func newStackCallInstance(call *StackCall, key addrs.InstanceKey, repetition instances.RepetitionData, deferred bool) *StackCallInstance {
4950
return &StackCallInstance{
5051
call: call,
5152
key: key,
53+
deferred: deferred,
5254
main: call.main,
5355
repetition: repetition,
5456
}
@@ -67,7 +69,7 @@ func (c *StackCallInstance) CalledStackAddr() stackaddrs.StackInstance {
6769

6870
func (c *StackCallInstance) Stack(ctx context.Context, phase EvalPhase) *Stack {
6971
stack, err := c.stack.For(phase).Do(ctx, c.tracingName(), func(ctx context.Context) (*Stack, error) {
70-
return newStack(c.main, c.CalledStackAddr(), c.call.stack, c.call.config.TargetConfig(), c.call.GetExternalRemovedBlocks()), nil
72+
return newStack(c.main, c.CalledStackAddr(), c.call.stack, c.call.config.TargetConfig(), c.call.GetExternalRemovedBlocks(), c.deferred), nil
7173
})
7274
if err != nil {
7375
// we don't have cycles in here, and we don't return an error so this

internal/stacks/stackruntime/internal/stackeval/walk_dynamic.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,39 @@ func walkDynamicObjectsInStack[Output any](
7070

7171
// If unknown, then process the unknown instance and skip the rest.
7272
if unknown {
73-
inst := call.UnknownInstance(ctx, phase)
74-
visit(ctx, walk, inst)
73+
knownInstances := stack.KnownEmbeddedStacks(call.addr.Item, phase)
74+
if len(knownInstances) == 0 {
75+
// with no known instances, we'll just process the constant
76+
// unknown instance
77+
inst := call.UnknownInstance(ctx, phase)
78+
visit(ctx, walk, inst)
7579

76-
childStack := inst.Stack(ctx, phase)
77-
walkDynamicObjectsInStack(ctx, walk, childStack, phase, visit)
78-
return
80+
childStack := inst.Stack(ctx, phase)
81+
walkDynamicObjectsInStack(ctx, walk, childStack, phase, visit)
82+
return
83+
}
84+
85+
// otherwise we have instances, so we'll process those with
86+
// dedicated instances
87+
88+
for inst := range knownInstances {
89+
if inst.Key == addrs.WildcardKey {
90+
inst := call.UnknownInstance(ctx, phase)
91+
visit(ctx, walk, inst)
92+
93+
childStack := inst.Stack(ctx, phase)
94+
walkDynamicObjectsInStack(ctx, walk, childStack, phase, visit)
95+
} else {
96+
inst := newStackCallInstance(call, inst.Key, instances.RepetitionData{
97+
EachKey: inst.Key.Value(),
98+
EachValue: cty.UnknownVal(cty.DynamicPseudoType),
99+
}, true)
100+
visit(ctx, walk, inst)
101+
102+
childStack := inst.Stack(ctx, phase)
103+
walkDynamicObjectsInStack(ctx, walk, childStack, phase, visit)
104+
}
105+
}
79106
}
80107

81108
// Otherwise, process the instances and their child stacks.

0 commit comments

Comments
 (0)