mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
330 lines
11 KiB
Go
330 lines
11 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package stackplan
|
|
|
|
import (
|
|
"iter"
|
|
"time"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/collections"
|
|
"github.com/hashicorp/terraform/internal/lang"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
|
)
|
|
|
|
// Plan is the main type in this package, representing an entire stack plan,
|
|
// or at least the subset of the information that Terraform needs to reliably
|
|
// apply the plan and detect any inconsistencies during the apply process.
|
|
//
|
|
// However, the process of _creating_ a plan doesn't actually produce a single
|
|
// object of this type, and instead produces fragments of it gradually as the
|
|
// planning process proceeds. The caller of the stack runtime must retain
|
|
// all of the raw parts in the order they were emitted and provide them back
|
|
// during the apply phase, and then we will finally construct a single instance
|
|
// of Plan covering the entire set of changes before we begin applying it.
|
|
type Plan struct {
|
|
// Applyable is true for a plan that was successfully created in full and
|
|
// is sufficient to be applied, or false if the plan is incomplete for
|
|
// some reason, such as if an error occurred during planning and so
|
|
// the planning process did not entirely run.
|
|
Applyable bool
|
|
|
|
// Complete is true for a plan that shouldn't need any follow-up plans to
|
|
// converge.
|
|
Complete bool
|
|
|
|
// Mode is the original mode of the plan.
|
|
Mode plans.Mode
|
|
|
|
// Root is the root StackInstance for the configuration being planned.
|
|
// The StackInstance object wraps the specific components for each stack
|
|
// instance.
|
|
Root *StackInstance
|
|
|
|
// The raw representation of the raw state that was provided in the request
|
|
// to create the plan. We use this primarily to perform mundane state
|
|
// data structure maintenence operations, such as discarding keys that
|
|
// are no longer needed or replacing data in old formats with the
|
|
// equivalent new representations.
|
|
PrevRunStateRaw map[string]*anypb.Any
|
|
|
|
// RootInputValues are the input variable values provided to calculate
|
|
// the plan. We must use the same values during the apply step to
|
|
// sure that the actions taken can be consistent with what was planned.
|
|
RootInputValues map[stackaddrs.InputVariable]cty.Value
|
|
|
|
// ApplyTimeInputVariables are the names of the root input variable
|
|
// values whose values must be re-supplied during the apply phase,
|
|
// instead of being persisted in [Plan.RootInputValues].
|
|
ApplyTimeInputVariables collections.Set[stackaddrs.InputVariable]
|
|
|
|
// DeletedInputVariables tracks the set of input variables that are being
|
|
// deleted by this plan. The apply operation will miss any values
|
|
// that are not defined in the configuration, but should still emit
|
|
// deletion events to remove them from the state.
|
|
DeletedInputVariables collections.Set[stackaddrs.InputVariable]
|
|
|
|
// DeletedOutputValues tracks the set of output values that are being
|
|
// deleted by this plan. The apply operation will miss any output values
|
|
// that are not defined in the configuration, but should still emit
|
|
// deletion events to remove them from the state. Output values not being
|
|
// deleted will be recomputed during the apply so are not needed.
|
|
DeletedOutputValues collections.Set[stackaddrs.OutputValue]
|
|
|
|
// DeletedComponents are a set of components that are in the state that
|
|
// should just be removed without any apply operation. This is typically
|
|
// because they are not referenced in the configuration and have no
|
|
// associated resources.
|
|
DeletedComponents collections.Set[stackaddrs.AbsComponentInstance]
|
|
|
|
// FunctionResults is a shared table of results from calling
|
|
// provider functions. This is stored and loaded from during the planning
|
|
// stage to use during apply operations.
|
|
FunctionResults []lang.FunctionResultHash
|
|
|
|
// PlanTimestamp is the time at which the plan was created.
|
|
PlanTimestamp time.Time
|
|
}
|
|
|
|
func (p *Plan) AllComponents() iter.Seq2[stackaddrs.AbsComponentInstance, *Component] {
|
|
return func(yield func(stackaddrs.AbsComponentInstance, *Component) bool) {
|
|
p.Root.iterate(yield)
|
|
}
|
|
}
|
|
|
|
func (p *Plan) ComponentInstanceAddresses(addr stackaddrs.AbsComponent) iter.Seq[stackaddrs.ComponentInstance] {
|
|
return func(yield func(stackaddrs.ComponentInstance) bool) {
|
|
stack := p.Root.GetDescendentStack(addr.Stack)
|
|
if stack != nil {
|
|
components := stack.Components[addr.Item]
|
|
for key := range components {
|
|
proceed := yield(stackaddrs.ComponentInstance{
|
|
Component: addr.Item,
|
|
Key: key,
|
|
})
|
|
if !proceed {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ComponentInstances returns a set of the component instances that belong to
|
|
// the given component.
|
|
func (p *Plan) ComponentInstances(addr stackaddrs.AbsComponent) iter.Seq2[stackaddrs.ComponentInstance, *Component] {
|
|
return func(yield func(stackaddrs.ComponentInstance, *Component) bool) {
|
|
stack := p.Root.GetDescendentStack(addr.Stack)
|
|
if stack != nil {
|
|
components := stack.Components[addr.Item]
|
|
for key, component := range components {
|
|
proceed := yield(stackaddrs.ComponentInstance{
|
|
Component: addr.Item,
|
|
Key: key,
|
|
}, component)
|
|
if !proceed {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Plan) StackInstances(addr stackaddrs.AbsStackCall) iter.Seq[stackaddrs.StackInstance] {
|
|
return func(yield func(stackaddrs.StackInstance) bool) {
|
|
stack := p.Root.GetDescendentStack(addr.Stack)
|
|
if stack != nil {
|
|
stacks := stack.Children[addr.Item.Name]
|
|
for key := range stacks {
|
|
proceed := yield(append(addr.Stack, stackaddrs.StackInstanceStep{
|
|
Name: addr.Item.Name,
|
|
Key: key,
|
|
}))
|
|
if !proceed {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Plan) GetOrCreate(addr stackaddrs.AbsComponentInstance, component *Component) *Component {
|
|
targetStackInstance := p.Root.GetOrCreateDescendentStack(addr.Stack)
|
|
return targetStackInstance.GetOrCreateComponent(addr.Item, component)
|
|
}
|
|
|
|
func (p *Plan) GetComponent(addr stackaddrs.AbsComponentInstance) *Component {
|
|
targetStackInstance := p.Root.GetDescendentStack(addr.Stack)
|
|
return targetStackInstance.GetComponent(addr.Item)
|
|
}
|
|
|
|
func (p *Plan) GetStack(addr stackaddrs.StackInstance) *StackInstance {
|
|
return p.Root.GetDescendentStack(addr)
|
|
}
|
|
|
|
// RequiredProviderInstances returns a description of all of the provider
|
|
// instance slots that are required to satisfy the resource instances
|
|
// belonging to the given component instance.
|
|
//
|
|
// See also stackeval.ComponentConfig.RequiredProviderInstances for a similar
|
|
// function that operates on the configuration of a component instance rather
|
|
// than the plan of one.
|
|
func (p *Plan) RequiredProviderInstances(addr stackaddrs.AbsComponentInstance) addrs.Set[addrs.RootProviderConfig] {
|
|
stack := p.Root.GetDescendentStack(addr.Stack)
|
|
if stack == nil {
|
|
return addrs.MakeSet[addrs.RootProviderConfig]()
|
|
}
|
|
|
|
components, ok := stack.Components[addr.Item.Component]
|
|
if !ok {
|
|
return addrs.MakeSet[addrs.RootProviderConfig]()
|
|
}
|
|
|
|
component, ok := components[addr.Item.Key]
|
|
if !ok {
|
|
return addrs.MakeSet[addrs.RootProviderConfig]()
|
|
}
|
|
return component.RequiredProviderInstances()
|
|
}
|
|
|
|
// StackInstance stores the components and embedded stacks for a single stack
|
|
// instance.
|
|
type StackInstance struct {
|
|
Address stackaddrs.StackInstance
|
|
Children map[string]map[addrs.InstanceKey]*StackInstance
|
|
Components map[stackaddrs.Component]map[addrs.InstanceKey]*Component
|
|
}
|
|
|
|
func newStackInstance(address stackaddrs.StackInstance) *StackInstance {
|
|
return &StackInstance{
|
|
Address: address,
|
|
Components: make(map[stackaddrs.Component]map[addrs.InstanceKey]*Component),
|
|
Children: make(map[string]map[addrs.InstanceKey]*StackInstance),
|
|
}
|
|
}
|
|
|
|
func (stack *StackInstance) GetComponent(addr stackaddrs.ComponentInstance) *Component {
|
|
components, ok := stack.Components[addr.Component]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return components[addr.Key]
|
|
}
|
|
|
|
func (stack *StackInstance) GetOrCreateComponent(addr stackaddrs.ComponentInstance, component *Component) *Component {
|
|
components, ok := stack.Components[addr.Component]
|
|
if !ok {
|
|
components = make(map[addrs.InstanceKey]*Component)
|
|
}
|
|
existing, ok := components[addr.Key]
|
|
if ok {
|
|
return existing
|
|
}
|
|
components[addr.Key] = component
|
|
stack.Components[addr.Component] = components
|
|
return component
|
|
}
|
|
|
|
func (stack *StackInstance) GetOrCreateDescendentStack(addr stackaddrs.StackInstance) *StackInstance {
|
|
if len(addr) == 0 {
|
|
return stack
|
|
}
|
|
next := stack.GetOrCreateChildStack(addr[0])
|
|
return next.GetOrCreateDescendentStack(addr[1:])
|
|
}
|
|
|
|
func (stack *StackInstance) GetOrCreateChildStack(step stackaddrs.StackInstanceStep) *StackInstance {
|
|
child := stack.GetChildStack(step)
|
|
if child == nil {
|
|
child = stack.CreateChildStack(step)
|
|
}
|
|
return child
|
|
}
|
|
|
|
func (stack *StackInstance) GetDescendentStack(addr stackaddrs.StackInstance) *StackInstance {
|
|
if len(addr) == 0 {
|
|
return stack
|
|
}
|
|
|
|
next := stack.GetChildStack(addr[0])
|
|
if next == nil {
|
|
return nil
|
|
}
|
|
return next.GetDescendentStack(addr[1:])
|
|
}
|
|
|
|
func (stack *StackInstance) GetChildStack(step stackaddrs.StackInstanceStep) *StackInstance {
|
|
insts, ok := stack.Children[step.Name]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return insts[step.Key]
|
|
}
|
|
|
|
func (stack *StackInstance) CreateChildStack(step stackaddrs.StackInstanceStep) *StackInstance {
|
|
stacks, ok := stack.Children[step.Name]
|
|
if !ok {
|
|
stacks = make(map[addrs.InstanceKey]*StackInstance)
|
|
}
|
|
stacks[step.Key] = newStackInstance(append(stack.Address, step))
|
|
stack.Children[step.Name] = stacks
|
|
return stacks[step.Key]
|
|
}
|
|
|
|
func (stack *StackInstance) GetOk(addr stackaddrs.AbsComponentInstance) (*Component, bool) {
|
|
if len(addr.Stack) == 0 {
|
|
component, ok := stack.Components[addr.Item.Component]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
instance, ok := component[addr.Item.Key]
|
|
return instance, ok
|
|
}
|
|
|
|
stacks, ok := stack.Children[addr.Stack[0].Name]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
next, ok := stacks[addr.Stack[0].Key]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
return next.GetOk(stackaddrs.AbsComponentInstance{
|
|
Stack: addr.Stack[1:],
|
|
Item: addr.Item,
|
|
})
|
|
}
|
|
|
|
func (stack *StackInstance) iterate(yield func(stackaddrs.AbsComponentInstance, *Component) bool) bool {
|
|
for name, components := range stack.Components {
|
|
for key, component := range components {
|
|
proceed := yield(stackaddrs.AbsComponentInstance{
|
|
Stack: stack.Address,
|
|
Item: stackaddrs.ComponentInstance{
|
|
Component: name,
|
|
Key: key,
|
|
},
|
|
}, component)
|
|
if !proceed {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, stacks := range stack.Children {
|
|
for _, inst := range stacks {
|
|
proceed := inst.iterate(yield)
|
|
if !proceed {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|