github.com/erriapo/terraform@v0.6.12-0.20160203182612-0340ea72354f/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  			if _, ok := v.(GraphNodeAddressable); ok {
    41  				if !targetedNodes.Include(v) {
    42  					log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
    43  					g.Remove(v)
    44  				}
    45  			}
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) {
    52  	addrs := make([]ResourceAddress, len(t.Targets))
    53  	for i, target := range t.Targets {
    54  		ta, err := ParseResourceAddress(target)
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		addrs[i] = *ta
    59  	}
    60  	return addrs, nil
    61  }
    62  
    63  // Returns the list of targeted nodes. A targeted node is either addressed
    64  // directly, or is an Ancestor of a targeted node. Destroy mode keeps
    65  // Descendents instead of Ancestors.
    66  func (t *TargetsTransformer) selectTargetedNodes(
    67  	g *Graph, addrs []ResourceAddress) (*dag.Set, error) {
    68  	targetedNodes := new(dag.Set)
    69  	for _, v := range g.Vertices() {
    70  		if t.nodeIsTarget(v, addrs) {
    71  			targetedNodes.Add(v)
    72  
    73  			// We inform nodes that ask about the list of targets - helps for nodes
    74  			// that need to dynamically expand. Note that this only occurs for nodes
    75  			// that are already directly targeted.
    76  			if tn, ok := v.(GraphNodeTargetable); ok {
    77  				tn.SetTargets(addrs)
    78  			}
    79  
    80  			var deps *dag.Set
    81  			var err error
    82  			if t.Destroy {
    83  				deps, err = g.Descendents(v)
    84  			} else {
    85  				deps, err = g.Ancestors(v)
    86  			}
    87  			if err != nil {
    88  				return nil, err
    89  			}
    90  
    91  			for _, d := range deps.List() {
    92  				targetedNodes.Add(d)
    93  			}
    94  		}
    95  	}
    96  	return targetedNodes, nil
    97  }
    98  
    99  func (t *TargetsTransformer) nodeIsTarget(
   100  	v dag.Vertex, addrs []ResourceAddress) bool {
   101  	r, ok := v.(GraphNodeAddressable)
   102  	if !ok {
   103  		return false
   104  	}
   105  	addr := r.ResourceAddress()
   106  	for _, targetAddr := range addrs {
   107  		if targetAddr.Equals(addr) {
   108  			return true
   109  		}
   110  	}
   111  	return false
   112  }