github.com/ojiry/terraform@v0.8.2-0.20161218223921-e50cec712c4a/terraform/transform_noop.go (about) 1 package terraform 2 3 import ( 4 "github.com/hashicorp/terraform/dag" 5 ) 6 7 // GraphNodeNoopPrunable can be implemented by nodes that can be 8 // pruned if they are noops. 9 type GraphNodeNoopPrunable interface { 10 Noop(*NoopOpts) bool 11 } 12 13 // NoopOpts are the options available to determine if your node is a noop. 14 type NoopOpts struct { 15 Graph *Graph 16 Vertex dag.Vertex 17 Diff *Diff 18 State *State 19 ModDiff *ModuleDiff 20 ModState *ModuleState 21 } 22 23 // PruneNoopTransformer is a graph transform that prunes nodes that 24 // consider themselves no-ops. This is done to both simplify the graph 25 // as well as to remove graph nodes that might otherwise cause problems 26 // during the graph run. Therefore, this transformer isn't completely 27 // an optimization step, and can instead be considered critical to 28 // Terraform operations. 29 // 30 // Example of the above case: variables for modules interpolate their values. 31 // Interpolation will fail on destruction (since attributes are being deleted), 32 // but variables shouldn't even eval if there is nothing that will consume 33 // the variable. Therefore, variables can note that they can be omitted 34 // safely in this case. 35 // 36 // The PruneNoopTransformer will prune nodes depth first, and will automatically 37 // create connect through the dependencies of pruned nodes. For example, 38 // if we have a graph A => B => C (A depends on B, etc.), and B decides to 39 // be removed, we'll still be left with A => C; the edge will be properly 40 // connected. 41 type PruneNoopTransformer struct { 42 Diff *Diff 43 State *State 44 } 45 46 func (t *PruneNoopTransformer) Transform(g *Graph) error { 47 // Find the leaves. 48 leaves := make([]dag.Vertex, 0, 10) 49 for _, v := range g.Vertices() { 50 if g.DownEdges(v).Len() == 0 { 51 leaves = append(leaves, v) 52 } 53 } 54 55 // Do a depth first walk from the leaves and remove things. 56 return g.ReverseDepthFirstWalk(leaves, func(v dag.Vertex, depth int) error { 57 // We need a prunable 58 pn, ok := v.(GraphNodeNoopPrunable) 59 if !ok { 60 return nil 61 } 62 63 // Start building the noop opts 64 path := g.Path 65 if pn, ok := v.(GraphNodeSubPath); ok { 66 path = pn.Path() 67 } 68 69 var modDiff *ModuleDiff 70 var modState *ModuleState 71 if t.Diff != nil { 72 modDiff = t.Diff.ModuleByPath(path) 73 } 74 if t.State != nil { 75 modState = t.State.ModuleByPath(path) 76 } 77 78 // Determine if its a noop. If it isn't, just return 79 noop := pn.Noop(&NoopOpts{ 80 Graph: g, 81 Vertex: v, 82 Diff: t.Diff, 83 State: t.State, 84 ModDiff: modDiff, 85 ModState: modState, 86 }) 87 if !noop { 88 return nil 89 } 90 91 // It is a noop! We first preserve edges. 92 up := g.UpEdges(v).List() 93 for _, downV := range g.DownEdges(v).List() { 94 for _, upV := range up { 95 g.Connect(dag.BasicEdge(upV, downV)) 96 } 97 } 98 99 // Then remove it 100 g.Remove(v) 101 102 return nil 103 }) 104 }