github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/context_apply.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/internal/addrs"
     8  	"github.com/hashicorp/terraform/internal/configs"
     9  	"github.com/hashicorp/terraform/internal/plans"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  	"github.com/hashicorp/terraform/internal/tfdiags"
    12  	"github.com/zclconf/go-cty/cty"
    13  )
    14  
    15  // Apply performs the actions described by the given Plan object and returns
    16  // the resulting updated state.
    17  //
    18  // The given configuration *must* be the same configuration that was passed
    19  // earlier to Context.Plan in order to create this plan.
    20  //
    21  // Even if the returned diagnostics contains errors, Apply always returns the
    22  // resulting state which is likely to have been partially-updated.
    23  func (c *Context) Apply(plan *plans.Plan, config *configs.Config) (*states.State, tfdiags.Diagnostics) {
    24  	defer c.acquireRun("apply")()
    25  
    26  	log.Printf("[DEBUG] Building and walking apply graph for %s plan", plan.UIMode)
    27  
    28  	if plan.Errored {
    29  		var diags tfdiags.Diagnostics
    30  		diags = diags.Append(tfdiags.Sourceless(
    31  			tfdiags.Error,
    32  			"Cannot apply failed plan",
    33  			`The given plan is incomplete due to errors during planning, and so it cannot be applied.`,
    34  		))
    35  		return nil, diags
    36  	}
    37  
    38  	graph, operation, diags := c.applyGraph(plan, config, true)
    39  	if diags.HasErrors() {
    40  		return nil, diags
    41  	}
    42  
    43  	workingState := plan.PriorState.DeepCopy()
    44  	walker, walkDiags := c.walk(graph, operation, &graphWalkOpts{
    45  		Config:     config,
    46  		InputState: workingState,
    47  		Changes:    plan.Changes,
    48  
    49  		// We need to propagate the check results from the plan phase,
    50  		// because that will tell us which checkable objects we're expecting
    51  		// to see updated results from during the apply step.
    52  		PlanTimeCheckResults: plan.Checks,
    53  	})
    54  	diags = diags.Append(walker.NonFatalDiagnostics)
    55  	diags = diags.Append(walkDiags)
    56  
    57  	// After the walk is finished, we capture a simplified snapshot of the
    58  	// check result data as part of the new state.
    59  	walker.State.RecordCheckResults(walker.Checks)
    60  
    61  	newState := walker.State.Close()
    62  	if plan.UIMode == plans.DestroyMode && !diags.HasErrors() {
    63  		// NOTE: This is a vestigial violation of the rule that we mustn't
    64  		// use plan.UIMode to affect apply-time behavior.
    65  		// We ideally ought to just call newState.PruneResourceHusks
    66  		// unconditionally here, but we historically didn't and haven't yet
    67  		// verified that it'd be safe to do so.
    68  		newState.PruneResourceHusks()
    69  	}
    70  
    71  	if len(plan.TargetAddrs) > 0 {
    72  		diags = diags.Append(tfdiags.Sourceless(
    73  			tfdiags.Warning,
    74  			"Applied changes may be incomplete",
    75  			`The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may not be fully updated. Run the following command to verify that no other changes are pending:
    76      terraform plan
    77  	
    78  Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`,
    79  		))
    80  	}
    81  
    82  	// FIXME: we cannot check for an empty plan for refresh-only, because root
    83  	// outputs are always stored as changes. The final condition of the state
    84  	// also depends on some cleanup which happens during the apply walk. It
    85  	// would probably make more sense if applying a refresh-only plan were
    86  	// simply just returning the planned state and checks, but some extra
    87  	// cleanup is going to be needed to make the plan state match what apply
    88  	// would do. For now we can copy the checks over which were overwritten
    89  	// during the apply walk.
    90  	// Despite the intent of UIMode, it must still be used for apply-time
    91  	// differences in destroy plans too, so we can make use of that here as
    92  	// well.
    93  	if plan.UIMode == plans.RefreshOnlyMode {
    94  		newState.CheckResults = plan.Checks.DeepCopy()
    95  	}
    96  
    97  	return newState, diags
    98  }
    99  
   100  func (c *Context) applyGraph(plan *plans.Plan, config *configs.Config, validate bool) (*Graph, walkOperation, tfdiags.Diagnostics) {
   101  	var diags tfdiags.Diagnostics
   102  
   103  	variables := InputValues{}
   104  	for name, dyVal := range plan.VariableValues {
   105  		val, err := dyVal.Decode(cty.DynamicPseudoType)
   106  		if err != nil {
   107  			diags = diags.Append(tfdiags.Sourceless(
   108  				tfdiags.Error,
   109  				"Invalid variable value in plan",
   110  				fmt.Sprintf("Invalid value for variable %q recorded in plan file: %s.", name, err),
   111  			))
   112  			continue
   113  		}
   114  
   115  		variables[name] = &InputValue{
   116  			Value:      val,
   117  			SourceType: ValueFromPlan,
   118  		}
   119  	}
   120  	if diags.HasErrors() {
   121  		return nil, walkApply, diags
   122  	}
   123  
   124  	// The plan.VariableValues field only records variables that were actually
   125  	// set by the caller in the PlanOpts, so we may need to provide
   126  	// placeholders for any other variables that the user didn't set, in
   127  	// which case Terraform will once again use the default value from the
   128  	// configuration when we visit these variables during the graph walk.
   129  	for name := range config.Module.Variables {
   130  		if _, ok := variables[name]; ok {
   131  			continue
   132  		}
   133  		variables[name] = &InputValue{
   134  			Value:      cty.NilVal,
   135  			SourceType: ValueFromPlan,
   136  		}
   137  	}
   138  
   139  	operation := walkApply
   140  	if plan.UIMode == plans.DestroyMode {
   141  		// FIXME: Due to differences in how objects must be handled in the
   142  		// graph and evaluated during a complete destroy, we must continue to
   143  		// use plans.DestroyMode to switch on this behavior. If all objects
   144  		// which require special destroy handling can be tracked in the plan,
   145  		// then this switch will no longer be needed and we can remove the
   146  		// walkDestroy operation mode.
   147  		// TODO: Audit that and remove walkDestroy as an operation mode.
   148  		operation = walkDestroy
   149  	}
   150  
   151  	graph, moreDiags := (&ApplyGraphBuilder{
   152  		Config:             config,
   153  		Changes:            plan.Changes,
   154  		State:              plan.PriorState,
   155  		RootVariableValues: variables,
   156  		Plugins:            c.plugins,
   157  		Targets:            plan.TargetAddrs,
   158  		ForceReplace:       plan.ForceReplaceAddrs,
   159  		Operation:          operation,
   160  	}).Build(addrs.RootModuleInstance)
   161  	diags = diags.Append(moreDiags)
   162  	if moreDiags.HasErrors() {
   163  		return nil, walkApply, diags
   164  	}
   165  
   166  	return graph, operation, diags
   167  }
   168  
   169  // ApplyGraphForUI is a last vestage of graphs in the public interface of
   170  // Context (as opposed to graphs as an implementation detail) intended only for
   171  // use by the "terraform graph" command when asked to render an apply-time
   172  // graph.
   173  //
   174  // The result of this is intended only for rendering ot the user as a dot
   175  // graph, and so may change in future in order to make the result more useful
   176  // in that context, even if drifts away from the physical graph that Terraform
   177  // Core currently uses as an implementation detail of planning.
   178  func (c *Context) ApplyGraphForUI(plan *plans.Plan, config *configs.Config) (*Graph, tfdiags.Diagnostics) {
   179  	// For now though, this really is just the internal graph, confusing
   180  	// implementation details and all.
   181  
   182  	var diags tfdiags.Diagnostics
   183  
   184  	graph, _, moreDiags := c.applyGraph(plan, config, false)
   185  	diags = diags.Append(moreDiags)
   186  	return graph, diags
   187  }