blob: ca650e63e8b5505c24cdd3aa2d4367b0ebe20a78 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package plans
import (
"sort"
"time"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/collections"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang/globalref"
"github.com/hashicorp/terraform/internal/moduletest/mocking"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
)
// Plan is the top-level type representing a planned set of changes.
//
// A plan is a summary of the set of changes required to move from a current
// state to a goal state derived from configuration. The described changes
// are not applied directly, but contain an approximation of the final
// result that will be completed during apply by resolving any values that
// cannot be predicted.
//
// A plan must always be accompanied by the configuration it was built from,
// since the plan does not itself include all of the information required to
// make the changes indicated.
type Plan struct {
// Mode is the mode under which this plan was created.
//
// This is only recorded to allow for UI differences when presenting plans
// to the end-user, and so it must not be used to influence apply-time
// behavior. The actions during apply must be described entirely by
// the Changes field, regardless of how the plan was created.
//
// FIXME: destroy operations still rely on DestroyMode being set, because
// there is no other source of this information in the plan. New behavior
// should not be added based on this flag, and changing the flag should be
// checked carefully against existing destroy behaviors.
UIMode Mode
// VariableValues, VariableMarks, and ApplyTimeVariables together describe
// how Terraform should decide the input variable values for the apply
// phase if this plan is to be applied.
//
// VariableValues and VariableMarks describe persisted (non-ephemeral)
// values that were set as part of the planning options and are to be
// re-used during the apply phase. VariableValues can potentially contain
// unknown values for a speculative plan, but the variable values must
// all be known for a plan that will subsequently be applied.
//
// ApplyTimeVariables retains the names of any ephemeral variables that were
// set (non-null) during the planning phase and must therefore be
// re-supplied by the caller (potentially with different values) during
// the apply phase. Ephemeral input variables are intended for populating
// arguments for other ephemeral objects in the configuration, such as
// provider configurations. Although the values for these variables can
// change between plan and apply, their "nullness" may not.
VariableValues map[string]DynamicValue
VariableMarks map[string][]cty.PathValueMarks
ApplyTimeVariables collections.Set[string]
Changes *ChangesSrc
DriftedResources []*ResourceInstanceChangeSrc
DeferredResources []*DeferredResourceInstanceChangeSrc
TargetAddrs []addrs.Targetable
ForceReplaceAddrs []addrs.AbsResourceInstance
Backend Backend
// Complete is true if Terraform considers this to be a "complete" plan,
// which is to say that it includes a planned action (even if no-op)
// for every resource instance object that was mentioned across both
// the desired state and prior state.
//
// If Complete is false then the plan might still be applyable (check
// [Plan.Applyable]) but after applying it the operator should be reminded
// to plan and apply again to hopefully make more progress towards
// convergence.
//
// For an incomplete plan, other fields of this type may give more context
// about why the plan is incomplete, which a UI layer could present to
// the user as part of a warning that the plan is incomplete.
Complete bool
// Applyable is true if both Terraform was able to create a plan
// successfully and if the plan calls for making some sort of meaningful
// change.
//
// If [Plan.Errored] is also set then that means the plan is non-applyable
// due to an error. If not then the plan was created successfully but found
// no material differences between desired and prior state, and so
// applying this plan would achieve nothing.
Applyable bool
// Errored is true if the Changes information is incomplete because
// the planning operation failed. An errored plan cannot be applied,
// but can be cautiously inspected for debugging purposes.
Errored bool
// Checks captures a snapshot of the (probably-incomplete) check results
// at the end of the planning process.
//
// If this plan is applyable (that is, if the planning process completed
// without errors) then the set of checks here should be complete even
// though some of them will likely have StatusUnknown where the check
// condition depends on values we won't know until the apply step.
Checks *states.CheckResults
// RelevantAttributes is a set of resource instance addresses and
// attributes that are either directly affected by proposed changes or may
// have indirectly contributed to them via references in expressions.
//
// This is the result of a heuristic and is intended only as a hint to
// the UI layer in case it wants to emphasize or de-emphasize certain
// resources. Don't use this to drive any non-cosmetic behavior, especially
// including anything that would be subject to compatibility constraints.
RelevantAttributes []globalref.ResourceAttr
// PrevRunState and PriorState both describe the situation that the plan
// was derived from:
//
// PrevRunState is a representation of the outcome of the previous
// Terraform operation, without any updates from the remote system but
// potentially including some changes that resulted from state upgrade
// actions.
//
// PriorState is a representation of the current state of remote objects,
// which will differ from PrevRunState if the "refresh" step returned
// different data, which might reflect drift.
//
// PriorState is the main snapshot we use for actions during apply.
// PrevRunState is only here so that we can diff PriorState against it in
// order to report to the user any out-of-band changes we've detected.
PrevRunState *states.State
PriorState *states.State
// ExternalReferences are references that are being made to resources within
// the plan from external sources.
//
// This is never recorded outside of Terraform. It is not written into the
// binary plan file, and it is not written into the JSON structured outputs.
// The testing framework never writes the plans out but holds everything in
// memory as it executes, so there is no need to add any kind of
// serialization for this field. This does mean that you shouldn't rely on
// this field existing unless you have just generated the plan.
ExternalReferences []*addrs.Reference
// Overrides contains the set of overrides that were applied while making
// this plan. We need to provide the same set of overrides when applying
// the plan so we preserve them here. As with ExternalReferences, this is
// only used by the testing framework and so isn't written into any external
// representation of the plan.
Overrides *mocking.Overrides
// Timestamp is the record of truth for when the plan happened.
Timestamp time.Time
// ProviderFunctionResults stores hashed results from all provider
// function calls, so that calls during apply can be checked for
// consistency.
ProviderFunctionResults []providers.FunctionHash
}
// ProviderAddrs returns a list of all of the provider configuration addresses
// referenced throughout the receiving plan.
//
// The result is de-duplicated so that each distinct address appears only once.
func (p *Plan) ProviderAddrs() []addrs.AbsProviderConfig {
if p == nil || p.Changes == nil {
return nil
}
m := map[string]addrs.AbsProviderConfig{}
for _, rc := range p.Changes.Resources {
m[rc.ProviderAddr.String()] = rc.ProviderAddr
}
if len(m) == 0 {
return nil
}
// This is mainly just so we'll get stable results for testing purposes.
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
ret := make([]addrs.AbsProviderConfig, len(keys))
for i, key := range keys {
ret[i] = m[key]
}
return ret
}
// Backend represents the backend-related configuration and other data as it
// existed when a plan was created.
type Backend struct {
// Type is the type of backend that the plan will apply against.
Type string
// Config is the configuration of the backend, whose schema is decided by
// the backend Type.
Config DynamicValue
// Workspace is the name of the workspace that was active when the plan
// was created. It is illegal to apply a plan created for one workspace
// to the state of another workspace.
// (This constraint is already enforced by the statefile lineage mechanism,
// but storing this explicitly allows us to return a better error message
// in the situation where the user has the wrong workspace selected.)
Workspace string
}
func NewBackend(typeName string, config cty.Value, configSchema *configschema.Block, workspaceName string) (*Backend, error) {
dv, err := NewDynamicValue(config, configSchema.ImpliedType())
if err != nil {
return nil, err
}
return &Backend{
Type: typeName,
Config: dv,
Workspace: workspaceName,
}, nil
}