github.com/blacked/terraform@v0.6.2-0.20150806163846-669c4ad71586/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 Targeting: (len(b.Targets) > 0), 111 }, 112 113 // Output-related transformations 114 &AddOutputOrphanTransformer{State: b.State}, 115 116 // Provider-related transformations 117 &MissingProviderTransformer{Providers: b.Providers}, 118 &ProviderTransformer{}, 119 &DisableProviderTransformer{}, 120 121 // Provisioner-related transformations 122 &MissingProvisionerTransformer{Provisioners: b.Provisioners}, 123 &ProvisionerTransformer{}, 124 125 // Run our vertex-level transforms 126 &VertexTransformer{ 127 Transforms: []GraphVertexTransformer{ 128 // Expand any statically expanded nodes, such as module graphs 129 &ExpandTransform{ 130 Builder: b, 131 }, 132 }, 133 }, 134 135 // Flatten stuff 136 &FlattenTransformer{}, 137 138 // Make sure all the connections that are proxies are connected through 139 &ProxyTransformer{}, 140 141 // Make sure we have a single root 142 &RootTransformer{}, 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 and provisioners. This must happen 154 // only once because flattened modules might depend on empty 155 // providers. 156 &PruneProviderTransformer{}, 157 &PruneProvisionerTransformer{}, 158 159 // Create the destruction nodes 160 &DestroyTransformer{FullDestroy: b.Destroy}, 161 &CreateBeforeDestroyTransformer{}, 162 b.conditional(&conditionalOpts{ 163 If: func() bool { return !b.Verbose }, 164 Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State}, 165 }), 166 167 // Remove the noop nodes 168 &PruneNoopTransformer{Diff: b.Diff, State: b.State}, 169 170 // Insert nodes to close opened plugin connections 171 &CloseProviderTransformer{}, 172 &CloseProvisionerTransformer{}, 173 174 // Make sure we have a single root after the above changes. 175 // This is the 2nd root transformer. In practice this shouldn't 176 // actually matter as the RootTransformer is idempotent. 177 &RootTransformer{}, 178 179 // Perform the transitive reduction to make our graph a bit 180 // more sane if possible (it usually is possible). 181 &TransitiveReductionTransformer{}, 182 ) 183 } 184 185 // Remove nils 186 for i, s := range steps { 187 if s == nil { 188 steps = append(steps[:i], steps[i+1:]...) 189 } 190 } 191 192 return steps 193 } 194 195 type conditionalOpts struct { 196 If func() bool 197 Then GraphTransformer 198 } 199 200 func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer { 201 if o.If != nil && o.Then != nil && o.If() { 202 return o.Then 203 } 204 return nil 205 }