github.com/bendemaree/terraform@v0.5.4-0.20150613200311-f50d97d6eee6/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 &PruneProviderTransformer{}, 120 &DisableProviderTransformer{}, 121 122 // Provisioner-related transformations 123 &MissingProvisionerTransformer{Provisioners: b.Provisioners}, 124 &ProvisionerTransformer{}, 125 &PruneProvisionerTransformer{}, 126 127 // Run our vertex-level transforms 128 &VertexTransformer{ 129 Transforms: []GraphVertexTransformer{ 130 // Expand any statically expanded nodes, such as module graphs 131 &ExpandTransform{ 132 Builder: b, 133 }, 134 }, 135 }, 136 137 // Flatten stuff 138 &FlattenTransformer{}, 139 140 // Make sure all the connections that are proxies are connected through 141 &ProxyTransformer{}, 142 143 // Optionally reduces the graph to a user-specified list of targets and 144 // their dependencies. 145 &TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy}, 146 147 // Make sure we have a single root 148 &RootTransformer{}, 149 } 150 151 // If we're on the root path, then we do a bunch of other stuff. 152 // We don't do the following for modules. 153 if len(path) <= 1 { 154 steps = append(steps, 155 // Create the destruction nodes 156 &DestroyTransformer{FullDestroy: b.Destroy}, 157 &CreateBeforeDestroyTransformer{}, 158 b.conditional(&conditionalOpts{ 159 If: func() bool { return !b.Verbose }, 160 Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State}, 161 }), 162 163 // Make sure we have a single root after the above changes. 164 // This is the 2nd root transformer. In practice this shouldn't 165 // actually matter as the RootTransformer is idempotent. 166 &RootTransformer{}, 167 168 // Perform the transitive reduction to make our graph a bit 169 // more sane if possible (it usually is possible). 170 &TransitiveReductionTransformer{}, 171 ) 172 } 173 174 // Remove nils 175 for i, s := range steps { 176 if s == nil { 177 steps = append(steps[:i], steps[i+1:]...) 178 } 179 } 180 181 return steps 182 } 183 184 type conditionalOpts struct { 185 If func() bool 186 Then GraphTransformer 187 } 188 189 func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer { 190 if o.If != nil && o.Then != nil && o.If() { 191 return o.Then 192 } 193 return nil 194 }