Skip to content

Commit

Permalink
Combine all plan graphs, including import
Browse files Browse the repository at this point in the history
Combine all plan-time graphs into a single graph builder, because
_everything is a plan_!

Convert the import graph to a plan graph. This should resolve a few edge
cases about things not being properly evaluated during import, and takes
a step towards being able to _plan_ an import.
  • Loading branch information
jbardin committed Jun 20, 2022
1 parent 93ff272 commit e97ae28
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 187 deletions.
5 changes: 4 additions & 1 deletion internal/terraform/context_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ func (c *Context) Import(config *configs.Config, prevRunState *states.State, opt
variables := opts.SetVariables

// Initialize our graph builder
builder := &ImportGraphBuilder{
builder := &PlanGraphBuilder{
ImportTargets: opts.Targets,
Config: config,
State: state,
RootVariableValues: variables,
Plugins: c.plugins,
skipRefresh: true,
Operation: walkImport,
}

// Build the graph
Expand Down
5 changes: 4 additions & 1 deletion internal/terraform/context_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
Targets: opts.Targets,
ForceReplace: opts.ForceReplace,
skipRefresh: opts.SkipRefresh,
Operation: walkPlan,
}).Build(addrs.RootModuleInstance)
return graph, walkPlan, diags
case plans.RefreshOnlyMode:
Expand All @@ -571,16 +572,18 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
Targets: opts.Targets,
skipRefresh: opts.SkipRefresh,
skipPlanChanges: true, // this activates "refresh only" mode.
Operation: walkPlan,
}).Build(addrs.RootModuleInstance)
return graph, walkPlan, diags
case plans.DestroyMode:
graph, diags := DestroyPlanGraphBuilder(&PlanGraphBuilder{
graph, diags := (&PlanGraphBuilder{
Config: config,
State: prevRunState,
RootVariableValues: opts.SetVariables,
Plugins: c.plugins,
Targets: opts.Targets,
skipRefresh: opts.SkipRefresh,
Operation: walkPlanDestroy,
}).Build(addrs.RootModuleInstance)
return graph, walkPlanDestroy, diags
default:
Expand Down
3 changes: 2 additions & 1 deletion internal/terraform/context_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
}
}

graph, moreDiags := ValidateGraphBuilder(&PlanGraphBuilder{
graph, moreDiags := (&PlanGraphBuilder{
Config: config,
Plugins: c.plugins,
State: states.NewState(),
RootVariableValues: varValues,
Operation: walkValidate,
}).Build(addrs.RootModuleInstance)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
Expand Down
2 changes: 1 addition & 1 deletion internal/terraform/context_walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con
refreshState = states.NewState().SyncWrapper()
prevRunState = states.NewState().SyncWrapper()

case walkPlan, walkPlanDestroy:
case walkPlan, walkPlanDestroy, walkImport:
state = inputState.DeepCopy().SyncWrapper()
refreshState = inputState.DeepCopy().SyncWrapper()
prevRunState = inputState.DeepCopy().SyncWrapper()
Expand Down
17 changes: 0 additions & 17 deletions internal/terraform/graph_builder_destroy_plan.go

This file was deleted.

101 changes: 0 additions & 101 deletions internal/terraform/graph_builder_import.go

This file was deleted.

103 changes: 88 additions & 15 deletions internal/terraform/graph_builder_plan.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package terraform

import (
"log"

"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
Expand Down Expand Up @@ -63,12 +65,16 @@ type PlanGraphBuilder struct {
ConcreteResourceInstanceDeposed ConcreteResourceInstanceDeposedNodeFunc
ConcreteModule ConcreteModuleNodeFunc

// destroy is set to true when create a full destroy plan.
destroy bool
// Plan Operation this graph will be used for.
Operation walkOperation

// ImportTargets are the list of resources to import.
ImportTargets []*ImportTarget
}

// See GraphBuilder
func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
log.Printf("[TRACE] building graph for %s", b.Operation)
return (&BasicGraphBuilder{
Steps: b.Steps(),
Name: "PlanGraphBuilder",
Expand All @@ -77,14 +83,29 @@ func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Dia

// See GraphBuilder
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
b.init()
switch b.Operation {
case walkPlan:
b.initPlan()
case walkPlanDestroy:
b.initDestroy()
case walkValidate:
b.initValidate()
case walkImport:
b.initImport()
default:
panic("invalid plan operation: " + b.Operation.String())
}

steps := []GraphTransformer{
// Creates all the resources represented in the config
&ConfigTransformer{
Concrete: b.ConcreteResource,
Config: b.Config,
skip: b.destroy,

// Resources are not added from the config on destroy.
skip: b.Operation == walkPlanDestroy,

importTargets: b.ImportTargets,
},

// Add dynamic values
Expand All @@ -94,21 +115,22 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
&OutputTransformer{
Config: b.Config,
RefreshOnly: b.skipPlanChanges,
removeRootOutputs: b.destroy,
removeRootOutputs: b.Operation == walkPlanDestroy,
},

// Add orphan resources
&OrphanResourceInstanceTransformer{
Concrete: b.ConcreteResourceOrphan,
State: b.State,
Config: b.Config,
skip: b.destroy,
skip: b.Operation == walkPlanDestroy,
},

// We also need nodes for any deposed instance objects present in the
// state, so we can plan to destroy them. (This intentionally
// skips creating nodes for _current_ objects, since ConfigTransformer
// created nodes that will do that during DynamicExpand.)
// state, so we can plan to destroy them. (During plan this will
// intentionally skips creating nodes for _current_ objects, since
// ConfigTransformer created nodes that will do that during
// DynamicExpand.)
&StateTransformer{
ConcreteCurrent: b.ConcreteResourceInstance,
ConcreteDeposed: b.ConcreteResourceInstanceDeposed,
Expand Down Expand Up @@ -172,12 +194,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
return steps
}

func (b *PlanGraphBuilder) init() {
// Do nothing if the user requests customizing the fields
if b.CustomConcrete {
return
}

func (b *PlanGraphBuilder) initPlan() {
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
Expand Down Expand Up @@ -210,5 +227,61 @@ func (b *PlanGraphBuilder) init() {
skipPlanChanges: b.skipPlanChanges,
}
}
}

func (b *PlanGraphBuilder) initDestroy() {
b.initPlan()

b.ConcreteResourceInstance = func(a *NodeAbstractResourceInstance) dag.Vertex {
return &NodePlanDestroyableResourceInstance{
NodeAbstractResourceInstance: a,
skipRefresh: b.skipRefresh,
}
}
}

func (b *PlanGraphBuilder) initValidate() {
// Set the provider to the normal provider. This will ask for input.
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}

b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &NodeValidatableResource{
NodeAbstractResource: a,
}
}

b.ConcreteModule = func(n *nodeExpandModule) dag.Vertex {
return &nodeValidateModule{
nodeExpandModule: *n,
}
}
}

func (b *PlanGraphBuilder) initImport() {
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}

b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &nodeExpandPlannableResource{
NodeAbstractResource: a,

// For now we always skip planning changes for import, since we are
// not going to combine importing with other changes. This is
// temporary to try and maintain existing import behaviors, but
// planning will need to be allowed for more complex configurations.
skipPlanChanges: true,

// We also skip refresh for now, since the plan output is written
// as the new state, and users are not expecting the import process
// to update any other instances in state.
skipRefresh: true,
}
}
}

0 comments on commit e97ae28

Please sign in to comment.