github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/graph_builder_plan.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/internal/addrs"
     7  	"github.com/hashicorp/terraform/internal/configs"
     8  	"github.com/hashicorp/terraform/internal/dag"
     9  	"github.com/hashicorp/terraform/internal/states"
    10  	"github.com/hashicorp/terraform/internal/tfdiags"
    11  )
    12  
    13  // PlanGraphBuilder is a GraphBuilder implementation that builds a graph for
    14  // planning and for other "plan-like" operations which don't require an
    15  // already-calculated plan as input.
    16  //
    17  // Unlike the apply graph builder, this graph builder:
    18  //
    19  //   - Makes its decisions primarily based on the given configuration, which
    20  //     represents the desired state.
    21  //
    22  //   - Ignores certain lifecycle concerns like create_before_destroy, because
    23  //     those are only important once we already know what action we're planning
    24  //     to take against a particular resource instance.
    25  type PlanGraphBuilder struct {
    26  	// Config is the configuration tree to build a plan from.
    27  	Config *configs.Config
    28  
    29  	// State is the current state
    30  	State *states.State
    31  
    32  	// RootVariableValues are the raw input values for root input variables
    33  	// given by the caller, which we'll resolve into final values as part
    34  	// of the plan walk.
    35  	RootVariableValues InputValues
    36  
    37  	// Plugins is a library of plug-in components (providers and
    38  	// provisioners) available for use.
    39  	Plugins *contextPlugins
    40  
    41  	// Targets are resources to target
    42  	Targets []addrs.Targetable
    43  
    44  	// ForceReplace are resource instances where if we would normally have
    45  	// generated a NoOp or Update action then we'll force generating a replace
    46  	// action instead. Create and Delete actions are not affected.
    47  	ForceReplace []addrs.AbsResourceInstance
    48  
    49  	// skipRefresh indicates that we should skip refreshing managed resources
    50  	skipRefresh bool
    51  
    52  	// skipPlanChanges indicates that we should skip the step of comparing
    53  	// prior state with configuration and generating planned changes to
    54  	// resource instances. (This is for the "refresh only" planning mode,
    55  	// where we _only_ do the refresh step.)
    56  	skipPlanChanges bool
    57  
    58  	ConcreteProvider                ConcreteProviderNodeFunc
    59  	ConcreteResource                ConcreteResourceNodeFunc
    60  	ConcreteResourceInstance        ConcreteResourceInstanceNodeFunc
    61  	ConcreteResourceOrphan          ConcreteResourceInstanceNodeFunc
    62  	ConcreteResourceInstanceDeposed ConcreteResourceInstanceDeposedNodeFunc
    63  	ConcreteModule                  ConcreteModuleNodeFunc
    64  
    65  	// Plan Operation this graph will be used for.
    66  	Operation walkOperation
    67  
    68  	// ImportTargets are the list of resources to import.
    69  	ImportTargets []*ImportTarget
    70  }
    71  
    72  // See GraphBuilder
    73  func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
    74  	log.Printf("[TRACE] building graph for %s", b.Operation)
    75  	return (&BasicGraphBuilder{
    76  		Steps: b.Steps(),
    77  		Name:  "PlanGraphBuilder",
    78  	}).Build(path)
    79  }
    80  
    81  // This has to be the function we call from within the server code
    82  func (b *PlanGraphBuilder) BuildFlatEarthGraph(path addrs.ModuleInstance) map[string]*configs.Resource {
    83  	return b.Config.Module.ManagedResources
    84  }
    85  
    86  // See GraphBuilder
    87  func (b *PlanGraphBuilder) Steps() []GraphTransformer {
    88  	switch b.Operation {
    89  	case walkPlan:
    90  		b.initPlan()
    91  	case walkPlanDestroy:
    92  		b.initDestroy()
    93  	case walkValidate:
    94  		b.initValidate()
    95  	case walkImport:
    96  		b.initImport()
    97  	default:
    98  		panic("invalid plan operation: " + b.Operation.String())
    99  	}
   100  
   101  	steps := []GraphTransformer{
   102  		// Creates all the resources represented in the config
   103  		&ConfigTransformer{
   104  			Concrete: b.ConcreteResource,
   105  			Config:   b.Config,
   106  
   107  			// Resources are not added from the config on destroy.
   108  			skip: b.Operation == walkPlanDestroy,
   109  
   110  			importTargets: b.ImportTargets,
   111  		},
   112  
   113  		// Add dynamic values
   114  		&RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues},
   115  		&ModuleVariableTransformer{Config: b.Config},
   116  		&LocalTransformer{Config: b.Config},
   117  		&OutputTransformer{
   118  			Config:            b.Config,
   119  			RefreshOnly:       b.skipPlanChanges,
   120  			removeRootOutputs: b.Operation == walkPlanDestroy,
   121  		},
   122  
   123  		// Add orphan resources
   124  		&OrphanResourceInstanceTransformer{
   125  			Concrete: b.ConcreteResourceOrphan,
   126  			State:    b.State,
   127  			Config:   b.Config,
   128  			skip:     b.Operation == walkPlanDestroy,
   129  		},
   130  
   131  		// We also need nodes for any deposed instance objects present in the
   132  		// state, so we can plan to destroy them. (During plan this will
   133  		// intentionally skip creating nodes for _current_ objects, since
   134  		// ConfigTransformer created nodes that will do that during
   135  		// DynamicExpand.)
   136  		&StateTransformer{
   137  			ConcreteCurrent: b.ConcreteResourceInstance,
   138  			ConcreteDeposed: b.ConcreteResourceInstanceDeposed,
   139  			State:           b.State,
   140  		},
   141  
   142  		// Attach the state
   143  		&AttachStateTransformer{State: b.State},
   144  
   145  		// Create orphan output nodes
   146  		&OrphanOutputTransformer{Config: b.Config, State: b.State},
   147  
   148  		// Attach the configuration to any resources
   149  		&AttachResourceConfigTransformer{Config: b.Config},
   150  
   151  		// add providers
   152  		transformProviders(b.ConcreteProvider, b.Config),
   153  
   154  		// Remove modules no longer present in the config
   155  		&RemovedModuleTransformer{Config: b.Config, State: b.State},
   156  
   157  		// Must attach schemas before ReferenceTransformer so that we can
   158  		// analyze the configuration to find references.
   159  		&AttachSchemaTransformer{Plugins: b.Plugins, Config: b.Config},
   160  
   161  		// Create expansion nodes for all of the module calls. This must
   162  		// come after all other transformers that create nodes representing
   163  		// objects that can belong to modules.
   164  		&ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config},
   165  
   166  		&ReferenceTransformer{},
   167  
   168  		&AttachDependenciesTransformer{},
   169  
   170  		// Make sure data sources are aware of any depends_on from the
   171  		// configuration
   172  		&attachDataResourceDependsOnTransformer{},
   173  
   174  		// DestroyEdgeTransformer is only required during a plan so that the
   175  		// TargetsTransformer can determine which nodes to keep in the graph.
   176  		&DestroyEdgeTransformer{},
   177  
   178  		// Target
   179  		&TargetsTransformer{Targets: b.Targets},
   180  
   181  		// Detect when create_before_destroy must be forced on for a particular
   182  		// node due to dependency edges, to avoid graph cycles during apply.
   183  		&ForcedCBDTransformer{},
   184  
   185  		// Close opened plugin connections
   186  		&CloseProviderTransformer{},
   187  
   188  		// Close the root module
   189  		&CloseRootModuleTransformer{},
   190  
   191  		// Perform the transitive reduction to make our graph a bit
   192  		// more understandable if possible (it usually is possible).
   193  		&TransitiveReductionTransformer{},
   194  	}
   195  
   196  	return steps
   197  }
   198  
   199  func (b *PlanGraphBuilder) initPlan() {
   200  	b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
   201  		return &NodeApplyableProvider{
   202  			NodeAbstractProvider: a,
   203  		}
   204  	}
   205  
   206  	b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
   207  		return &nodeExpandPlannableResource{
   208  			NodeAbstractResource: a,
   209  			skipRefresh:          b.skipRefresh,
   210  			skipPlanChanges:      b.skipPlanChanges,
   211  			forceReplace:         b.ForceReplace,
   212  		}
   213  	}
   214  
   215  	b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex {
   216  		return &NodePlannableResourceInstanceOrphan{
   217  			NodeAbstractResourceInstance: a,
   218  			skipRefresh:                  b.skipRefresh,
   219  			skipPlanChanges:              b.skipPlanChanges,
   220  		}
   221  	}
   222  
   223  	b.ConcreteResourceInstanceDeposed = func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
   224  		return &NodePlanDeposedResourceInstanceObject{
   225  			NodeAbstractResourceInstance: a,
   226  			DeposedKey:                   key,
   227  
   228  			skipRefresh:     b.skipRefresh,
   229  			skipPlanChanges: b.skipPlanChanges,
   230  		}
   231  	}
   232  }
   233  
   234  func (b *PlanGraphBuilder) initDestroy() {
   235  	b.initPlan()
   236  
   237  	b.ConcreteResourceInstance = func(a *NodeAbstractResourceInstance) dag.Vertex {
   238  		return &NodePlanDestroyableResourceInstance{
   239  			NodeAbstractResourceInstance: a,
   240  			skipRefresh:                  b.skipRefresh,
   241  		}
   242  	}
   243  }
   244  
   245  func (b *PlanGraphBuilder) initValidate() {
   246  	// Set the provider to the normal provider. This will ask for input.
   247  	b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
   248  		return &NodeApplyableProvider{
   249  			NodeAbstractProvider: a,
   250  		}
   251  	}
   252  
   253  	b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
   254  		return &NodeValidatableResource{
   255  			NodeAbstractResource: a,
   256  		}
   257  	}
   258  
   259  	b.ConcreteModule = func(n *nodeExpandModule) dag.Vertex {
   260  		return &nodeValidateModule{
   261  			nodeExpandModule: *n,
   262  		}
   263  	}
   264  }
   265  
   266  func (b *PlanGraphBuilder) initImport() {
   267  	b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
   268  		return &NodeApplyableProvider{
   269  			NodeAbstractProvider: a,
   270  		}
   271  	}
   272  
   273  	b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
   274  		return &nodeExpandPlannableResource{
   275  			NodeAbstractResource: a,
   276  
   277  			// For now we always skip planning changes for import, since we are
   278  			// not going to combine importing with other changes. This is
   279  			// temporary to try and maintain existing import behaviors, but
   280  			// planning will need to be allowed for more complex configurations.
   281  			skipPlanChanges: true,
   282  
   283  			// We also skip refresh for now, since the plan output is written
   284  			// as the new state, and users are not expecting the import process
   285  			// to update any other instances in state.
   286  			skipRefresh: true,
   287  		}
   288  	}
   289  }