+// DestroyReferenceTransformer is a GraphTransformer that reverses the edges
+// for locals and outputs that depend on other nodes which will be
+// removed during destroy. If a destroy node is evaluated before the local or
+// output value, it will be removed from the state, and the later interpolation
+// will fail.
+type DestroyValueReferenceTransformer struct{}
+
+func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error {
+ vs := g.Vertices()
+ for _, v := range vs {
+ switch v.(type) {
+ case *NodeApplyableOutput, *NodeLocal:
+ // OK
+ default:
+ continue
+ }
+
+ // reverse any outgoing edges so that the value is evaluated first.
+ for _, e := range g.EdgesFrom(v) {
+ target := e.Target()
+
+ // only destroy nodes will be evaluated in reverse
+ if _, ok := target.(GraphNodeDestroyer); !ok {
+ continue
+ }
+
+ log.Printf("[TRACE] output dep: %s", dag.VertexName(target))
+
+ g.RemoveEdge(e)
+ g.Connect(&DestroyEdge{S: target, T: v})
+ }
+ }
+
+ return nil
+}
+
+// PruneUnusedValuesTransformer is s GraphTransformer that removes local and
+// output values which are not referenced in the graph. Since outputs and
+// locals always need to be evaluated, if they reference a resource that is not
+// available in the state the interpolation could fail.
+type PruneUnusedValuesTransformer struct{}
+
+func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
+ // this might need multiple runs in order to ensure that pruning a value
+ // doesn't effect a previously checked value.
+ for removed := 0; ; removed = 0 {
+ for _, v := range g.Vertices() {
+ switch v.(type) {
+ case *NodeApplyableOutput, *NodeLocal:
+ // OK
+ default:
+ continue
+ }
+
+ dependants := g.UpEdges(v)
+
+ switch dependants.Len() {
+ case 0:
+ // nothing at all depends on this
+ g.Remove(v)
+ removed++
+ case 1:
+ // because an output's destroy node always depends on the output,
+ // we need to check for the case of a single destroy node.
+ d := dependants.List()[0]
+ if _, ok := d.(*NodeDestroyableOutput); ok {
+ g.Remove(v)
+ removed++
+ }
+ }
+ }
+ if removed == 0 {
+ break
+ }
+ }
+
+ return nil
+}
+