github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/terraform/transform_targets.go (about)

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