github.com/nov1n/terraform@v0.7.9-0.20161103151050-bf6852f38e28/terraform/graph_builder.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform/config/module" 7 ) 8 9 // GraphBuilder is an interface that can be implemented and used with 10 // Terraform to build the graph that Terraform walks. 11 type GraphBuilder interface { 12 // Build builds the graph for the given module path. It is up to 13 // the interface implementation whether this build should expand 14 // the graph or not. 15 Build(path []string) (*Graph, error) 16 } 17 18 // BasicGraphBuilder is a GraphBuilder that builds a graph out of a 19 // series of transforms and (optionally) validates the graph is a valid 20 // structure. 21 type BasicGraphBuilder struct { 22 Steps []GraphTransformer 23 Validate bool 24 } 25 26 func (b *BasicGraphBuilder) Build(path []string) (*Graph, error) { 27 g := &Graph{Path: path} 28 for _, step := range b.Steps { 29 if step == nil { 30 continue 31 } 32 33 if err := step.Transform(g); err != nil { 34 return g, err 35 } 36 37 log.Printf( 38 "[TRACE] Graph after step %T:\n\n%s", 39 step, g.StringWithNodeTypes()) 40 } 41 42 // Validate the graph structure 43 if b.Validate { 44 if err := g.Validate(); err != nil { 45 log.Printf("[ERROR] Graph validation failed. Graph:\n\n%s", g.String()) 46 return nil, err 47 } 48 } 49 50 return g, nil 51 } 52 53 // BuiltinGraphBuilder is responsible for building the complete graph that 54 // Terraform uses for execution. It is an opinionated builder that defines 55 // the step order required to build a complete graph as is used and expected 56 // by Terraform. 57 // 58 // If you require a custom graph, you'll have to build it up manually 59 // on your own by building a new GraphBuilder implementation. 60 type BuiltinGraphBuilder struct { 61 // Root is the root module of the graph to build. 62 Root *module.Tree 63 64 // Diff is the diff. The proper module diffs will be looked up. 65 Diff *Diff 66 67 // State is the global state. The proper module states will be looked 68 // up by graph path. 69 State *State 70 71 // Providers is the list of providers supported. 72 Providers []string 73 74 // Provisioners is the list of provisioners supported. 75 Provisioners []string 76 77 // Targets is the user-specified list of resources to target. 78 Targets []string 79 80 // Destroy is set to true when we're in a `terraform destroy` or a 81 // `terraform plan -destroy` 82 Destroy bool 83 84 // Determines whether the GraphBuilder should perform graph validation before 85 // returning the Graph. Generally you want this to be done, except when you'd 86 // like to inspect a problematic graph. 87 Validate bool 88 89 // Verbose is set to true when the graph should be built "worst case", 90 // skipping any prune steps. This is used for early cycle detection during 91 // Validate and for manual inspection via `terraform graph -verbose`. 92 Verbose bool 93 } 94 95 // Build builds the graph according to the steps returned by Steps. 96 func (b *BuiltinGraphBuilder) Build(path []string) (*Graph, error) { 97 basic := &BasicGraphBuilder{ 98 Steps: b.Steps(path), 99 Validate: b.Validate, 100 } 101 102 return basic.Build(path) 103 } 104 105 // Steps returns the ordered list of GraphTransformers that must be executed 106 // to build a complete graph. 107 func (b *BuiltinGraphBuilder) Steps(path []string) []GraphTransformer { 108 steps := []GraphTransformer{ 109 // Create all our resources from the configuration and state 110 &ConfigTransformer{Module: b.Root}, 111 &OrphanTransformer{ 112 State: b.State, 113 Module: b.Root, 114 }, 115 116 // Output-related transformations 117 &AddOutputOrphanTransformer{State: b.State}, 118 119 // Provider-related transformations 120 &MissingProviderTransformer{Providers: b.Providers}, 121 &ProviderTransformer{}, 122 &DisableProviderTransformerOld{}, 123 124 // Provisioner-related transformations 125 &MissingProvisionerTransformer{Provisioners: b.Provisioners}, 126 &ProvisionerTransformer{}, 127 128 // Run our vertex-level transforms 129 &VertexTransformer{ 130 Transforms: []GraphVertexTransformer{ 131 // Expand any statically expanded nodes, such as module graphs 132 &ExpandTransform{ 133 Builder: b, 134 }, 135 }, 136 }, 137 138 // Flatten stuff 139 &FlattenTransformer{}, 140 141 // Make sure all the connections that are proxies are connected through 142 &ProxyTransformer{}, 143 } 144 145 // If we're on the root path, then we do a bunch of other stuff. 146 // We don't do the following for modules. 147 if len(path) <= 1 { 148 steps = append(steps, 149 // Optionally reduces the graph to a user-specified list of targets and 150 // their dependencies. 151 &TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy}, 152 153 // Prune the providers. This must happen only once because flattened 154 // modules might depend on empty providers. 155 &PruneProviderTransformer{}, 156 157 // Create the destruction nodes 158 &DestroyTransformer{FullDestroy: b.Destroy}, 159 b.conditional(&conditionalOpts{ 160 If: func() bool { return !b.Destroy }, 161 Then: &CreateBeforeDestroyTransformer{}, 162 }), 163 b.conditional(&conditionalOpts{ 164 If: func() bool { return !b.Verbose }, 165 Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State}, 166 }), 167 168 // Remove the noop nodes 169 &PruneNoopTransformer{Diff: b.Diff, State: b.State}, 170 171 // Insert nodes to close opened plugin connections 172 &CloseProviderTransformer{}, 173 &CloseProvisionerTransformer{}, 174 175 // Perform the transitive reduction to make our graph a bit 176 // more sane if possible (it usually is possible). 177 &TransitiveReductionTransformer{}, 178 ) 179 } 180 181 // Make sure we have a single root 182 steps = append(steps, &RootTransformer{}) 183 184 // Remove nils 185 for i, s := range steps { 186 if s == nil { 187 steps = append(steps[:i], steps[i+1:]...) 188 } 189 } 190 191 return steps 192 } 193 194 type conditionalOpts struct { 195 If func() bool 196 Then GraphTransformer 197 } 198 199 func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer { 200 if o.If != nil && o.Then != nil && o.If() { 201 return o.Then 202 } 203 return nil 204 }