github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/terraform/transform_targets.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/dag"
     7  )
     8  
     9  // TargetsTransformer is a GraphTransformer that, when the user specifies a
    10  // list of resources to target, limits the graph to only those resources and
    11  // their dependencies.
    12  type TargetsTransformer struct {
    13  	// List of targeted resource names specified by the user
    14  	Targets []string
    15  
    16  	// List of parsed targets, provided by callers like ResourceCountTransform
    17  	// that already have the targets parsed
    18  	ParsedTargets []ResourceAddress
    19  
    20  	// Set to true when we're in a `terraform destroy` or a
    21  	// `terraform plan -destroy`
    22  	Destroy bool
    23  }
    24  
    25  func (t *TargetsTransformer) Transform(g *Graph) error {
    26  	if len(t.Targets) > 0 && len(t.ParsedTargets) == 0 {
    27  		addrs, err := t.parseTargetAddresses()
    28  		if err != nil {
    29  			return err
    30  		}
    31  		t.ParsedTargets = addrs
    32  	}
    33  	if len(t.ParsedTargets) > 0 {
    34  		targetedNodes, err := t.selectTargetedNodes(g, t.ParsedTargets)
    35  		if err != nil {
    36  			return err
    37  		}
    38  
    39  		for _, v := range g.Vertices() {
    40  			removable := false
    41  			if _, ok := v.(GraphNodeAddressable); ok {
    42  				removable = true
    43  			}
    44  			if vr, ok := v.(RemovableIfNotTargeted); ok {
    45  				removable = vr.RemoveIfNotTargeted()
    46  			}
    47  			if removable && !targetedNodes.Include(v) {
    48  				log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
    49  				g.Remove(v)
    50  			}
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) {
    57  	addrs := make([]ResourceAddress, len(t.Targets))
    58  	for i, target := range t.Targets {
    59  		ta, err := ParseResourceAddress(target)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		addrs[i] = *ta
    64  	}
    65  	return addrs, nil
    66  }
    67  
    68  // Returns the list of targeted nodes. A targeted node is either addressed
    69  // directly, or is an Ancestor of a targeted node. Destroy mode keeps
    70  // Descendents instead of Ancestors.
    71  func (t *TargetsTransformer) selectTargetedNodes(
    72  	g *Graph, addrs []ResourceAddress) (*dag.Set, error) {
    73  	targetedNodes := new(dag.Set)
    74  	for _, v := range g.Vertices() {
    75  		if t.nodeIsTarget(v, addrs) {
    76  			targetedNodes.Add(v)
    77  
    78  			// We inform nodes that ask about the list of targets - helps for nodes
    79  			// that need to dynamically expand. Note that this only occurs for nodes
    80  			// that are already directly targeted.
    81  			if tn, ok := v.(GraphNodeTargetable); ok {
    82  				tn.SetTargets(addrs)
    83  			}
    84  
    85  			var deps *dag.Set
    86  			var err error
    87  			if t.Destroy {
    88  				deps, err = g.Descendents(v)
    89  			} else {
    90  				deps, err = g.Ancestors(v)
    91  			}
    92  			if err != nil {
    93  				return nil, err
    94  			}
    95  
    96  			for _, d := range deps.List() {
    97  				targetedNodes.Add(d)
    98  			}
    99  		}
   100  	}
   101  	return targetedNodes, nil
   102  }
   103  
   104  func (t *TargetsTransformer) nodeIsTarget(
   105  	v dag.Vertex, addrs []ResourceAddress) bool {
   106  	r, ok := v.(GraphNodeAddressable)
   107  	if !ok {
   108  		return false
   109  	}
   110  	addr := r.ResourceAddress()
   111  	for _, targetAddr := range addrs {
   112  		if targetAddr.Equals(addr) {
   113  			return true
   114  		}
   115  	}
   116  	return false
   117  }
   118  
   119  // RemovableIfNotTargeted is a special interface for graph nodes that
   120  // aren't directly addressable, but need to be removed from the graph when they
   121  // are not targeted. (Nodes that are not directly targeted end up in the set of
   122  // targeted nodes because something that _is_ targeted depends on them.) The
   123  // initial use case for this interface is GraphNodeConfigVariable, which was
   124  // having trouble interpolating for module variables in targeted scenarios that
   125  // filtered out the resource node being referenced.
   126  type RemovableIfNotTargeted interface {
   127  	RemoveIfNotTargeted() bool
   128  }