package terraform import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" ) // ApplyGraphBuilder implements GraphBuilder and is responsible for building // a graph for applying a Terraform diff. // // Because the graph is built from the diff (vs. the config or state), // this helps ensure that the apply-time graph doesn't modify any resources // that aren't explicitly in the diff. There are other scenarios where the // diff can be deviated, so this is just one layer of protection. type ApplyGraphBuilder struct { // Config is the configuration tree that the diff was built from. Config *configs.Config // Changes describes the changes that we need apply. Changes *plans.Changes // State is the current state State *states.State // Components is a factory for the plug-in components (providers and // provisioners) available for use. Components contextComponentFactory // Schemas is the repository of schemas we will draw from to analyse // the configuration. Schemas *Schemas // Targets are resources to target. This is only required to make sure // unnecessary outputs aren't included in the apply graph. The plan // builder successfully handles targeting resources. In the future, // outputs should go into the diff so that this is unnecessary. Targets []addrs.Targetable // DisableReduce, if true, will not reduce the graph. Great for testing. DisableReduce bool // Destroy, if true, represents a pure destroy operation Destroy bool // Validate will do structural validation of the graph. Validate bool } // See GraphBuilder func (b *ApplyGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { return (&BasicGraphBuilder{ Steps: b.Steps(), Validate: b.Validate, Name: "ApplyGraphBuilder", }).Build(path) } // See GraphBuilder func (b *ApplyGraphBuilder) Steps() []GraphTransformer { // Custom factory for creating providers. concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { return &NodeApplyableProvider{ NodeAbstractProvider: a, } } concreteResource := func(a *NodeAbstractResource) dag.Vertex { return &NodeApplyableResource{ NodeAbstractResource: a, } } concreteOrphanResource := func(a *NodeAbstractResource) dag.Vertex { return &NodeDestroyResource{ NodeAbstractResource: a, } } concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { return &NodeApplyableResourceInstance{ NodeAbstractResourceInstance: a, } } steps := []GraphTransformer{ // Creates all the resources represented in the config. During apply, // we use this just to ensure that the whole-resource metadata is // updated to reflect things such as whether the count argument is // set in config, or which provider configuration manages each resource. &ConfigTransformer{ Concrete: concreteResource, Config: b.Config, }, // Creates all the resource instances represented in the diff, along // with dependency edges against the whole-resource nodes added by // ConfigTransformer above. &DiffTransformer{ Concrete: concreteResourceInstance, State: b.State, Changes: b.Changes, }, // Creates extra cleanup nodes for any entire resources that are // no longer present in config, so we can make sure we clean up the // leftover empty resource states after the instances have been // destroyed. // (We don't track this particular type of change in the plan because // it's just cleanup of our own state object, and so doesn't effect // any real remote objects or consumable outputs.) &OrphanResourceTransformer{ Concrete: concreteOrphanResource, Config: b.Config, State: b.State, }, // Create orphan output nodes &OrphanOutputTransformer{Config: b.Config, State: b.State}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, // Attach the state &AttachStateTransformer{State: b.State}, // Provisioner-related transformations &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &ProvisionerTransformer{}, // Add root variables &RootVariableTransformer{Config: b.Config}, // Add the local values &LocalTransformer{Config: b.Config}, // Add the outputs &OutputTransformer{Config: b.Config}, // Add module variables &ModuleVariableTransformer{Config: b.Config}, // add providers TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), // Remove modules no longer present in the config &RemovedModuleTransformer{Config: b.Config, State: b.State}, // Must attach schemas before ReferenceTransformer so that we can // analyze the configuration to find references. &AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config}, // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. &ModuleExpansionTransformer{Config: b.Config}, // Connect references so ordering is correct &ReferenceTransformer{}, &AttachDependenciesTransformer{}, // Destruction ordering &DestroyEdgeTransformer{ Config: b.Config, State: b.State, Schemas: b.Schemas, }, &CBDEdgeTransformer{ Config: b.Config, State: b.State, Schemas: b.Schemas, }, // Create a destroy node for outputs to remove them from the state. &DestroyOutputTransformer{Destroy: b.Destroy}, // Prune unreferenced values, which may have interpolations that can't // be resolved. &PruneUnusedValuesTransformer{ Destroy: b.Destroy, }, // Add the node to fix the state count boundaries &CountBoundaryTransformer{ Config: b.Config, }, // Target &TargetsTransformer{Targets: b.Targets}, // Close opened plugin connections &CloseProviderTransformer{}, &CloseProvisionerTransformer{}, // Single root &RootTransformer{}, } if !b.DisableReduce { // Perform the transitive reduction to make our graph a bit // more sane if possible (it usually is possible). steps = append(steps, &TransitiveReductionTransformer{}) } return steps }