github.com/ibm-cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/terraform/transform_destroy_edge.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/config/module"
     7  	"github.com/hashicorp/terraform/dag"
     8  )
     9  
    10  // GraphNodeDestroyer must be implemented by nodes that destroy resources.
    11  type GraphNodeDestroyer interface {
    12  	dag.Vertex
    13  
    14  	// ResourceAddr is the address of the resource that is being
    15  	// destroyed by this node. If this returns nil, then this node
    16  	// is not destroying anything.
    17  	DestroyAddr() *ResourceAddress
    18  }
    19  
    20  // GraphNodeCreator must be implemented by nodes that create OR update resources.
    21  type GraphNodeCreator interface {
    22  	// ResourceAddr is the address of the resource being created or updated
    23  	CreateAddr() *ResourceAddress
    24  }
    25  
    26  // DestroyEdgeTransformer is a GraphTransformer that creates the proper
    27  // references for destroy resources. Destroy resources are more complex
    28  // in that they must be depend on the destruction of resources that
    29  // in turn depend on the CREATION of the node being destroy.
    30  //
    31  // That is complicated. Visually:
    32  //
    33  //   B_d -> A_d -> A -> B
    34  //
    35  // Notice that A destroy depends on B destroy, while B create depends on
    36  // A create. They're inverted. This must be done for example because often
    37  // dependent resources will block parent resources from deleting. Concrete
    38  // example: VPC with subnets, the VPC can't be deleted while there are
    39  // still subnets.
    40  type DestroyEdgeTransformer struct {
    41  	// These are needed to properly build the graph of dependencies
    42  	// to determine what a destroy node depends on. Any of these can be nil.
    43  	Module *module.Tree
    44  	State  *State
    45  }
    46  
    47  func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
    48  	log.Printf("[TRACE] DestroyEdgeTransformer: Beginning destroy edge transformation...")
    49  
    50  	// Build a map of what is being destroyed (by address string) to
    51  	// the list of destroyers. In general there will only be one destroyer
    52  	// but to make it more robust we support multiple.
    53  	destroyers := make(map[string][]GraphNodeDestroyer)
    54  	for _, v := range g.Vertices() {
    55  		dn, ok := v.(GraphNodeDestroyer)
    56  		if !ok {
    57  			continue
    58  		}
    59  
    60  		addr := dn.DestroyAddr()
    61  		if addr == nil {
    62  			continue
    63  		}
    64  
    65  		key := addr.String()
    66  		log.Printf(
    67  			"[TRACE] DestroyEdgeTransformer: %s destroying %q",
    68  			dag.VertexName(dn), key)
    69  		destroyers[key] = append(destroyers[key], dn)
    70  	}
    71  
    72  	// If we aren't destroying anything, there will be no edges to make
    73  	// so just exit early and avoid future work.
    74  	if len(destroyers) == 0 {
    75  		return nil
    76  	}
    77  
    78  	// Go through and connect creators to destroyers. Going along with
    79  	// our example, this makes: A_d => A
    80  	for _, v := range g.Vertices() {
    81  		cn, ok := v.(GraphNodeCreator)
    82  		if !ok {
    83  			continue
    84  		}
    85  
    86  		addr := cn.CreateAddr()
    87  		if addr == nil {
    88  			continue
    89  		}
    90  
    91  		key := addr.String()
    92  		ds := destroyers[key]
    93  		if len(ds) == 0 {
    94  			continue
    95  		}
    96  
    97  		for _, d := range ds {
    98  			// For illustrating our example
    99  			a_d := d.(dag.Vertex)
   100  			a := v
   101  
   102  			log.Printf(
   103  				"[TRACE] DestroyEdgeTransformer: connecting creator/destroyer: %s, %s",
   104  				dag.VertexName(a), dag.VertexName(a_d))
   105  
   106  			g.Connect(&DestroyEdge{S: a, T: a_d})
   107  		}
   108  	}
   109  
   110  	// This is strange but is the easiest way to get the dependencies
   111  	// of a node that is being destroyed. We use another graph to make sure
   112  	// the resource is in the graph and ask for references. We have to do this
   113  	// because the node that is being destroyed may NOT be in the graph.
   114  	//
   115  	// Example: resource A is force new, then destroy A AND create A are
   116  	// in the graph. BUT if resource A is just pure destroy, then only
   117  	// destroy A is in the graph, and create A is not.
   118  	providerFn := func(a *NodeAbstractProvider) dag.Vertex {
   119  		return &NodeApplyableProvider{NodeAbstractProvider: a}
   120  	}
   121  	steps := []GraphTransformer{
   122  		// Add outputs and metadata
   123  		&OutputTransformer{Module: t.Module},
   124  		&AttachResourceConfigTransformer{Module: t.Module},
   125  		&AttachStateTransformer{State: t.State},
   126  
   127  		// Add providers since they can affect destroy order as well
   128  		&MissingProviderTransformer{AllowAny: true, Concrete: providerFn},
   129  		&ProviderTransformer{},
   130  		&DisableProviderTransformer{},
   131  		&ParentProviderTransformer{},
   132  		&AttachProviderConfigTransformer{Module: t.Module},
   133  
   134  		// Add all the variables. We can depend on resources through
   135  		// variables due to module parameters, and we need to properly
   136  		// determine that.
   137  		&RootVariableTransformer{Module: t.Module},
   138  		&ModuleVariableTransformer{Module: t.Module},
   139  
   140  		&ReferenceTransformer{},
   141  	}
   142  
   143  	// Go through all the nodes being destroyed and create a graph.
   144  	// The resulting graph is only of things being CREATED. For example,
   145  	// following our example, the resulting graph would be:
   146  	//
   147  	//   A, B (with no edges)
   148  	//
   149  	var tempG Graph
   150  	var tempDestroyed []dag.Vertex
   151  	for d, _ := range destroyers {
   152  		// d is what is being destroyed. We parse the resource address
   153  		// which it came from it is a panic if this fails.
   154  		addr, err := ParseResourceAddress(d)
   155  		if err != nil {
   156  			panic(err)
   157  		}
   158  
   159  		// This part is a little bit weird but is the best way to
   160  		// find the dependencies we need to: build a graph and use the
   161  		// attach config and state transformers then ask for references.
   162  		abstract := &NodeAbstractResource{Addr: addr}
   163  		tempG.Add(abstract)
   164  		tempDestroyed = append(tempDestroyed, abstract)
   165  
   166  		// We also add the destroy version here since the destroy can
   167  		// depend on things that the creation doesn't (destroy provisioners).
   168  		destroy := &NodeDestroyResource{NodeAbstractResource: abstract}
   169  		tempG.Add(destroy)
   170  		tempDestroyed = append(tempDestroyed, destroy)
   171  	}
   172  
   173  	// Run the graph transforms so we have the information we need to
   174  	// build references.
   175  	for _, s := range steps {
   176  		if err := s.Transform(&tempG); err != nil {
   177  			return err
   178  		}
   179  	}
   180  
   181  	log.Printf("[TRACE] DestroyEdgeTransformer: reference graph: %s", tempG.String())
   182  
   183  	// Go through all the nodes in the graph and determine what they
   184  	// depend on.
   185  	for _, v := range tempDestroyed {
   186  		// Find all ancestors of this to determine the edges we'll depend on
   187  		vs, err := tempG.Ancestors(v)
   188  		if err != nil {
   189  			return err
   190  		}
   191  
   192  		refs := make([]dag.Vertex, 0, vs.Len())
   193  		for _, raw := range vs.List() {
   194  			refs = append(refs, raw.(dag.Vertex))
   195  		}
   196  
   197  		refNames := make([]string, len(refs))
   198  		for i, ref := range refs {
   199  			refNames[i] = dag.VertexName(ref)
   200  		}
   201  		log.Printf(
   202  			"[TRACE] DestroyEdgeTransformer: creation node %q references %s",
   203  			dag.VertexName(v), refNames)
   204  
   205  		// If we have no references, then we won't need to do anything
   206  		if len(refs) == 0 {
   207  			continue
   208  		}
   209  
   210  		// Get the destroy node for this. In the example of our struct,
   211  		// we are currently at B and we're looking for B_d.
   212  		rn, ok := v.(GraphNodeResource)
   213  		if !ok {
   214  			continue
   215  		}
   216  
   217  		addr := rn.ResourceAddr()
   218  		if addr == nil {
   219  			continue
   220  		}
   221  
   222  		dns := destroyers[addr.String()]
   223  
   224  		// We have dependencies, check if any are being destroyed
   225  		// to build the list of things that we must depend on!
   226  		//
   227  		// In the example of the struct, if we have:
   228  		//
   229  		//   B_d => A_d => A => B
   230  		//
   231  		// Then at this point in the algorithm we started with B_d,
   232  		// we built B (to get dependencies), and we found A. We're now looking
   233  		// to see if A_d exists.
   234  		var depDestroyers []dag.Vertex
   235  		for _, v := range refs {
   236  			rn, ok := v.(GraphNodeResource)
   237  			if !ok {
   238  				continue
   239  			}
   240  
   241  			addr := rn.ResourceAddr()
   242  			if addr == nil {
   243  				continue
   244  			}
   245  
   246  			key := addr.String()
   247  			if ds, ok := destroyers[key]; ok {
   248  				for _, d := range ds {
   249  					depDestroyers = append(depDestroyers, d.(dag.Vertex))
   250  					log.Printf(
   251  						"[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s",
   252  						key, dag.VertexName(d))
   253  				}
   254  			}
   255  		}
   256  
   257  		// Go through and make the connections. Use the variable
   258  		// names "a_d" and "b_d" to reference our example.
   259  		for _, a_d := range dns {
   260  			for _, b_d := range depDestroyers {
   261  				if b_d != a_d {
   262  					g.Connect(dag.BasicEdge(b_d, a_d))
   263  				}
   264  			}
   265  		}
   266  	}
   267  
   268  	return nil
   269  }