github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/transform_orphan_count.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  // OrphanResourceCountTransformer is a GraphTransformer that adds orphans
    13  // for an expanded count to the graph. The determination of this depends
    14  // on the count argument given.
    15  //
    16  // Orphans are found by comparing the count to what is found in the state.
    17  // This transform assumes that if an element in the state is within the count
    18  // bounds given, that it is not an orphan.
    19  type OrphanResourceCountTransformer struct {
    20  	Concrete ConcreteResourceInstanceNodeFunc
    21  
    22  	Count   int                  // Actual count of the resource, or -1 if count is not set at all
    23  	ForEach map[string]cty.Value // The ForEach map on the resource
    24  	Addr    addrs.AbsResource    // Addr of the resource to look for orphans
    25  	State   *states.State        // Full global state
    26  }
    27  
    28  func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
    29  	rs := t.State.Resource(t.Addr)
    30  	if rs == nil {
    31  		return nil // Resource doesn't exist in state, so nothing to do!
    32  	}
    33  
    34  	haveKeys := make(map[addrs.InstanceKey]struct{})
    35  	for key := range rs.Instances {
    36  		haveKeys[key] = struct{}{}
    37  	}
    38  
    39  	// if for_each is set, use that transformer
    40  	if t.ForEach != nil {
    41  		return t.transformForEach(haveKeys, g)
    42  	}
    43  	if t.Count < 0 {
    44  		return t.transformNoCount(haveKeys, g)
    45  	}
    46  	if t.Count == 0 {
    47  		return t.transformZeroCount(haveKeys, g)
    48  	}
    49  	return t.transformCount(haveKeys, g)
    50  }
    51  
    52  func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
    53  	// If there is a NoKey node, add this to the graph first,
    54  	// so that we can create edges to it in subsequent (StringKey) nodes.
    55  	// This is because the last item determines the resource mode for the whole resource,
    56  	// (see SetResourceInstanceCurrent for more information) and we need to evaluate
    57  	// an orphaned (NoKey) resource before the in-memory state is updated
    58  	// to deal with a new for_each resource
    59  	_, hasNoKeyNode := haveKeys[addrs.NoKey]
    60  	var noKeyNode dag.Vertex
    61  	if hasNoKeyNode {
    62  		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(addrs.NoKey))
    63  		noKeyNode = abstract
    64  		if f := t.Concrete; f != nil {
    65  			noKeyNode = f(abstract)
    66  		}
    67  		g.Add(noKeyNode)
    68  	}
    69  
    70  	for key := range haveKeys {
    71  		// If the key is no-key, we have already added it, so skip
    72  		if key == addrs.NoKey {
    73  			continue
    74  		}
    75  
    76  		s, _ := key.(addrs.StringKey)
    77  		// If the key is present in our current for_each, carry on
    78  		if _, ok := t.ForEach[string(s)]; ok {
    79  			continue
    80  		}
    81  
    82  		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
    83  		var node dag.Vertex = abstract
    84  		if f := t.Concrete; f != nil {
    85  			node = f(abstract)
    86  		}
    87  		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
    88  		g.Add(node)
    89  
    90  		// Add edge to noKeyNode if it exists
    91  		if hasNoKeyNode {
    92  			g.Connect(dag.BasicEdge(node, noKeyNode))
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
    99  	// Due to the logic in Transform, we only get in here if our count is
   100  	// at least one.
   101  
   102  	_, have0Key := haveKeys[addrs.IntKey(0)]
   103  
   104  	for key := range haveKeys {
   105  		if key == addrs.NoKey && !have0Key {
   106  			// If we have no 0-key then we will accept a no-key instance
   107  			// as an alias for it.
   108  			continue
   109  		}
   110  
   111  		i, isInt := key.(addrs.IntKey)
   112  		if isInt && int(i) < t.Count {
   113  			continue
   114  		}
   115  
   116  		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
   117  		var node dag.Vertex = abstract
   118  		if f := t.Concrete; f != nil {
   119  			node = f(abstract)
   120  		}
   121  		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
   122  		g.Add(node)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
   129  	// This case is easy: we need to orphan any keys we have at all.
   130  
   131  	for key := range haveKeys {
   132  		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
   133  		var node dag.Vertex = abstract
   134  		if f := t.Concrete; f != nil {
   135  			node = f(abstract)
   136  		}
   137  		log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node)
   138  		g.Add(node)
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
   145  	// Negative count indicates that count is not set at all, in which
   146  	// case we expect to have a single instance with no key set at all.
   147  	// However, we'll also accept an instance with key 0 set as an alias
   148  	// for it, in case the user has just deleted the "count" argument and
   149  	// so wants to keep the first instance in the set.
   150  
   151  	_, haveNoKey := haveKeys[addrs.NoKey]
   152  	_, have0Key := haveKeys[addrs.IntKey(0)]
   153  	keepKey := addrs.NoKey
   154  	if have0Key && !haveNoKey {
   155  		// If we don't have a no-key instance then we can use the 0-key instance
   156  		// instead.
   157  		keepKey = addrs.IntKey(0)
   158  	}
   159  
   160  	for key := range haveKeys {
   161  		if key == keepKey {
   162  			continue
   163  		}
   164  
   165  		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
   166  		var node dag.Vertex = abstract
   167  		if f := t.Concrete; f != nil {
   168  			node = f(abstract)
   169  		}
   170  		log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node)
   171  		g.Add(node)
   172  	}
   173  
   174  	return nil
   175  }