github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/context_apply.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/muratcelep/terraform/not-internal/addrs"
     8  	"github.com/muratcelep/terraform/not-internal/configs"
     9  	"github.com/muratcelep/terraform/not-internal/plans"
    10  	"github.com/muratcelep/terraform/not-internal/states"
    11  	"github.com/muratcelep/terraform/not-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  	var diags tfdiags.Diagnostics
    26  
    27  	log.Printf("[DEBUG] Building and walking apply graph for %s plan", plan.UIMode)
    28  
    29  	graph, operation, moreDiags := c.applyGraph(plan, config, true)
    30  	if moreDiags.HasErrors() {
    31  		return nil, diags
    32  	}
    33  
    34  	variables := InputValues{}
    35  	for name, dyVal := range plan.VariableValues {
    36  		val, err := dyVal.Decode(cty.DynamicPseudoType)
    37  		if err != nil {
    38  			diags = diags.Append(tfdiags.Sourceless(
    39  				tfdiags.Error,
    40  				"Invalid variable value in plan",
    41  				fmt.Sprintf("Invalid value for variable %q recorded in plan file: %s.", name, err),
    42  			))
    43  			continue
    44  		}
    45  
    46  		variables[name] = &InputValue{
    47  			Value:      val,
    48  			SourceType: ValueFromPlan,
    49  		}
    50  	}
    51  
    52  	workingState := plan.PriorState.DeepCopy()
    53  	walker, walkDiags := c.walk(graph, operation, &graphWalkOpts{
    54  		Config:             config,
    55  		InputState:         workingState,
    56  		Changes:            plan.Changes,
    57  		RootVariableValues: variables,
    58  	})
    59  	diags = diags.Append(walker.NonFatalDiagnostics)
    60  	diags = diags.Append(walkDiags)
    61  
    62  	newState := walker.State.Close()
    63  	if plan.UIMode == plans.DestroyMode && !diags.HasErrors() {
    64  		// NOTE: This is a vestigial violation of the rule that we mustn't
    65  		// use plan.UIMode to affect apply-time behavior.
    66  		// We ideally ought to just call newState.PruneResourceHusks
    67  		// unconditionally here, but we historically didn't and haven't yet
    68  		// verified that it'd be safe to do so.
    69  		newState.PruneResourceHusks()
    70  	}
    71  
    72  	if len(plan.TargetAddrs) > 0 {
    73  		diags = diags.Append(tfdiags.Sourceless(
    74  			tfdiags.Warning,
    75  			"Applied changes may be incomplete",
    76  			`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:
    77      terraform plan
    78  	
    79  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.`,
    80  		))
    81  	}
    82  
    83  	return newState, diags
    84  }
    85  
    86  func (c *Context) applyGraph(plan *plans.Plan, config *configs.Config, validate bool) (*Graph, walkOperation, tfdiags.Diagnostics) {
    87  	graph, diags := (&ApplyGraphBuilder{
    88  		Config:       config,
    89  		Changes:      plan.Changes,
    90  		State:        plan.PriorState,
    91  		Plugins:      c.plugins,
    92  		Targets:      plan.TargetAddrs,
    93  		ForceReplace: plan.ForceReplaceAddrs,
    94  		Validate:     validate,
    95  	}).Build(addrs.RootModuleInstance)
    96  
    97  	operation := walkApply
    98  	if plan.UIMode == plans.DestroyMode {
    99  		// NOTE: This is a vestigial violation of the rule that we mustn't
   100  		// use plan.UIMode to affect apply-time behavior. It's a design error
   101  		// if anything downstream switches behavior when operation is set
   102  		// to walkDestroy, but we've not yet fully audited that.
   103  		// TODO: Audit that and remove walkDestroy as an operation mode.
   104  		operation = walkDestroy
   105  	}
   106  
   107  	return graph, operation, diags
   108  }
   109  
   110  // ApplyGraphForUI is a last vestage of graphs in the public interface of
   111  // Context (as opposed to graphs as an implementation detail) intended only for
   112  // use by the "terraform graph" command when asked to render an apply-time
   113  // graph.
   114  //
   115  // The result of this is intended only for rendering ot the user as a dot
   116  // graph, and so may change in future in order to make the result more useful
   117  // in that context, even if drifts away from the physical graph that Terraform
   118  // Core currently uses as an implementation detail of planning.
   119  func (c *Context) ApplyGraphForUI(plan *plans.Plan, config *configs.Config) (*Graph, tfdiags.Diagnostics) {
   120  	// For now though, this really is just the not-internal graph, confusing
   121  	// implementation details and all.
   122  
   123  	var diags tfdiags.Diagnostics
   124  
   125  	graph, _, moreDiags := c.applyGraph(plan, config, false)
   126  	diags = diags.Append(moreDiags)
   127  	return graph, diags
   128  }