github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/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.StringWithNodeTypes()) 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 141 // If we're on the root path, then we do a bunch of other stuff. 142 // We don't do the following for modules. 143 if len(path) <= 1 { 144 steps = append(steps, 145 // Optionally reduces the graph to a user-specified list of targets and 146 // their dependencies. 147 &TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy}, 148 149 // Prune the providers. This must happen only once because flattened 150 // modules might depend on empty providers. 151 &PruneProviderTransformer{}, 152 153 // Create the destruction nodes 154 &DestroyTransformer{FullDestroy: b.Destroy}, 155 b.conditional(&conditionalOpts{ 156 If: func() bool { return !b.Destroy }, 157 Then: &CreateBeforeDestroyTransformer{}, 158 }), 159 b.conditional(&conditionalOpts{ 160 If: func() bool { return !b.Verbose }, 161 Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State}, 162 }), 163 164 // Remove the noop nodes 165 &PruneNoopTransformer{Diff: b.Diff, State: b.State}, 166 167 // Insert nodes to close opened plugin connections 168 &CloseProviderTransformer{}, 169 &CloseProvisionerTransformer{}, 170 171 // Perform the transitive reduction to make our graph a bit 172 // more sane if possible (it usually is possible). 173 &TransitiveReductionTransformer{}, 174 ) 175 } 176 177 // Make sure we have a single root 178 steps = append(steps, &RootTransformer{}) 179 180 // Remove nils 181 for i, s := range steps { 182 if s == nil { 183 steps = append(steps[:i], steps[i+1:]...) 184 } 185 } 186 187 return steps 188 } 189 190 type conditionalOpts struct { 191 If func() bool 192 Then GraphTransformer 193 } 194 195 func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer { 196 if o.If != nil && o.Then != nil && o.If() { 197 return o.Then 198 } 199 return nil 200 }