blob: 7f1e9bf1fa451ce12e0c854cceeabdad15fee77d [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// 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
}