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