Skip to content
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
87 changes: 56 additions & 31 deletions internal/stacks/stackruntime/internal/stackeval/input_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,43 +45,59 @@ func newInputVariable(main *Main, addr stackaddrs.AbsInputVariable, stack *Stack
}

// DefinedByStackCallInstance returns the stack call which ought to provide
// the definition (i.e. the final value) of this input variable.
// the definition (i.e. the final value) of this input variable. The source
// of the stack could either be a regular stack call instance or a removed
// stack call instance. One of the two will be returned. They are mutually
// exclusive as it is an error for two blocks to create the same stack instance.
//
// Returns nil if this input variable belongs to the main stack, because
// the main stack's input variables come from the planning options instead.
// Also returns nil if the reciever belongs to a stack config instance
//
// Also returns nil if the receiver belongs to a stack config instance
// that isn't actually declared in the configuration, which typically suggests
// that we don't yet know the number of instances of one of the stack calls
// along the chain.
func (v *InputVariable) DefinedByStackCallInstance(ctx context.Context, phase EvalPhase) *StackCallInstance {
func (v *InputVariable) DefinedByStackCallInstance(ctx context.Context, phase EvalPhase) (*StackCallInstance, *RemovedStackCallInstance) {
declarerAddr := v.addr.Stack
if declarerAddr.IsRoot() {
return nil
return nil, nil
}

callAddr := declarerAddr.Call()
callerCalls := v.stack.parent.EmbeddedStackCalls()
call := callerCalls[callAddr.Item]
if call == nil {
// Suggests that we're descended from a stack call that doesn't
// actually exist, which is odd but we'll tolerate it.
return nil
}

lastStep := declarerAddr[len(declarerAddr)-1]
instKey := lastStep.Key
if call := v.stack.parent.EmbeddedStackCall(callAddr.Item); call != nil {
lastStep := declarerAddr[len(declarerAddr)-1]
instKey := lastStep.Key

callInsts, unknown := call.Instances(ctx, phase)
if unknown {
// Return our static unknown instance for this variable.
return call.UnknownInstance(ctx, instKey, phase), nil
}
if inst, ok := callInsts[instKey]; ok {
return inst, nil
}

callInsts, unknown := call.Instances(ctx, phase)
if unknown {
// Return our static unknown instance for this variable.
return call.UnknownInstance(ctx, instKey, phase)
// otherwise, let's check if we have any removed calls that match the
// target instance
}
if callInsts == nil {
// Could get here if the call's for_each is invalid.
return nil

if calls := v.stack.parent.RemovedEmbeddedStackCall(callAddr.Item); calls != nil {
for _, call := range calls {
callInsts, unknown := call.InstancesFor(ctx, v.stack.addr, phase)
if unknown {
return nil, call.UnknownInstance(ctx, v.stack.addr, phase)
}
for _, inst := range callInsts {
// because we used the exact v.stack.addr in InstancesFor above
// then we should have at most one entry here if there were any
// matches.
return nil, inst
}
}
}

return callInsts[instKey]
return nil, nil
}

func (v *InputVariable) Value(ctx context.Context, phase EvalPhase) cty.Value {
Expand Down Expand Up @@ -175,23 +191,32 @@ func (v *InputVariable) CheckValue(ctx context.Context, phase EvalPhase) (cty.Va
return cfg.markValue(val), diags

default:
definedByCallInst := v.DefinedByStackCallInstance(ctx, phase)
if definedByCallInst == nil {
definedByCallInst, definedByRemovedCallInst := v.DefinedByStackCallInstance(ctx, phase)
switch {
case definedByCallInst != nil:
allVals := definedByCallInst.InputVariableValues(ctx, phase)
val := allVals.GetAttr(v.addr.Item.Name)

// TODO: check the value against any custom validation rules
// declared in the configuration.

return cfg.markValue(val), diags
case definedByRemovedCallInst != nil:
allVals, _ := definedByRemovedCallInst.InputVariableValues(ctx, phase)
val := allVals.GetAttr(v.addr.Item.Name)

// TODO: check the value against any custom validation rules
// declared in the configuration.

return cfg.markValue(val), diags
default:
// We seem to belong to a call instance that doesn't actually
// exist in the configuration. That either means that
// something's gone wrong or we are descended from a stack
// call whose instances aren't known yet; we'll assume
// the latter and return a placeholder.
return cfg.markValue(cty.UnknownVal(v.config.config.Type.Constraint)), diags
}

allVals := definedByCallInst.InputVariableValues(ctx, phase)
val := allVals.GetAttr(v.addr.Item.Name)

// TODO: check the value against any custom validation rules
// declared in the configuration.

return cfg.markValue(val), diags
}
},
)
Expand Down
18 changes: 11 additions & 7 deletions internal/stacks/stackruntime/internal/stackeval/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,7 @@ func (m *Main) MainStack() *Stack {
defer m.mu.Unlock()

if m.mainStack == nil {

mode := plans.NormalMode
if m.Planning() {
mode = m.PlanningOpts().PlanningMode
}

m.mainStack = newStack(m, stackaddrs.RootStackInstance, nil, config, newRemoved(), mode, false)
m.mainStack = newStack(m, stackaddrs.RootStackInstance, nil, config, newRemoved(), m.PlanningMode(), false)
}
return m.mainStack
}
Expand Down Expand Up @@ -621,6 +615,16 @@ func (m *Main) PlanTimestamp() time.Time {
return time.Now().UTC()
}

func (m *Main) PlanningMode() plans.Mode {
if m.applying != nil {
return m.applying.plan.Mode
}
if m.planning != nil {
return m.planning.opts.PlanningMode
}
return plans.NormalMode
}

// DependencyLocks returns the dependency locks for the given phase.
func (m *Main) DependencyLocks(phase EvalPhase) *depsfile.Locks {
switch phase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,15 @@ func (r *RemovedComponentInstance) ModuleTreePlan(ctx context.Context) (*plans.P
}
}

mode := plans.DestroyMode
if r.main.PlanningOpts().PlanningMode == plans.RefreshOnlyMode {
mode = plans.RefreshOnlyMode
}

plantimestamp := r.main.PlanTimestamp()
forget := !r.call.config.config.Destroy
opts := &terraform.PlanOpts{
Mode: plans.DestroyMode,
Mode: mode,
SetVariables: r.PlanPrevInputs(),
ExternalProviders: providerClients,
DeferralAllowed: true,
Expand Down Expand Up @@ -244,10 +249,11 @@ func (r *RemovedComponentInstance) PlanChanges(ctx context.Context) ([]stackplan

var changes []stackplan.PlannedChange
if plan != nil {
var action plans.Action
if r.call.config.config.Destroy {
action = plans.Delete
} else {
action := plans.Delete
switch {
case r.main.PlanningOpts().PlanningMode == plans.RefreshOnlyMode:
action = plans.Read
case !r.call.config.config.Destroy:
action = plans.Forget
}
changes, moreDiags = stackplan.FromPlan(ctx, r.ModuleTree(ctx), plan, nil, action, r)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ func newRemovedStackCallInstance(call *RemovedStackCall, from stackaddrs.StackIn

func (r *RemovedStackCallInstance) Stack(ctx context.Context, phase EvalPhase) *Stack {
stack, err := r.stack.For(phase).Do(ctx, r.from.String()+" create", func(ctx context.Context) (*Stack, error) {
return newStack(r.main, r.from, r.call.stack, r.call.config.TargetConfig(), r.call.GetExternalRemovedBlocks(), plans.DestroyMode, r.deferred), nil

mode := plans.DestroyMode
if r.main.PlanningMode() == plans.RefreshOnlyMode {
mode = plans.RefreshOnlyMode
}

return newStack(r.main, r.from, r.call.stack, r.call.config.TargetConfig(), r.call.GetExternalRemovedBlocks(), mode, r.deferred), nil
})
if err != nil {
// we never return an error from within the once call, so this shouldn't
Expand Down
Loading