aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_count.go
blob: 4f323a7a0ed20aedf2c0c0ee9035b53dc2842757 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package terraform

import (
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/states"
	"github.com/zclconf/go-cty/cty"
)

// OrphanResourceCountTransformer is a GraphTransformer that adds orphans
// for an expanded count to the graph. The determination of this depends
// on the count argument given.
//
// Orphans are found by comparing the count to what is found in the state.
// This transform assumes that if an element in the state is within the count
// bounds given, that it is not an orphan.
type OrphanResourceCountTransformer struct {
	Concrete ConcreteResourceInstanceNodeFunc

	Count   int                  // Actual count of the resource, or -1 if count is not set at all
	ForEach map[string]cty.Value // The ForEach map on the resource
	Addr    addrs.AbsResource    // Addr of the resource to look for orphans
	State   *states.State        // Full global state
}

func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
	rs := t.State.Resource(t.Addr)
	if rs == nil {
		return nil // Resource doesn't exist in state, so nothing to do!
	}

	haveKeys := make(map[addrs.InstanceKey]struct{})
	for key := range rs.Instances {
		haveKeys[key] = struct{}{}
	}

	// if for_each is set, use that transformer
	if t.ForEach != nil {
		return t.transformForEach(haveKeys, g)
	}
	if t.Count < 0 {
		return t.transformNoCount(haveKeys, g)
	}
	if t.Count == 0 {
		return t.transformZeroCount(haveKeys, g)
	}
	return t.transformCount(haveKeys, g)
}

func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	for key := range haveKeys {
		s, _ := key.(addrs.StringKey)
		// If the key is present in our current for_each, carry on
		if _, ok := t.ForEach[string(s)]; ok {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}
	return nil
}

func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Due to the logic in Transform, we only get in here if our count is
	// at least one.

	_, have0Key := haveKeys[addrs.IntKey(0)]

	for key := range haveKeys {
		if key == addrs.NoKey && !have0Key {
			// If we have no 0-key then we will accept a no-key instance
			// as an alias for it.
			continue
		}

		i, isInt := key.(addrs.IntKey)
		if isInt && int(i) < t.Count {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// This case is easy: we need to orphan any keys we have at all.

	for key := range haveKeys {
		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Negative count indicates that count is not set at all, in which
	// case we expect to have a single instance with no key set at all.
	// However, we'll also accept an instance with key 0 set as an alias
	// for it, in case the user has just deleted the "count" argument and
	// so wants to keep the first instance in the set.

	_, haveNoKey := haveKeys[addrs.NoKey]
	_, have0Key := haveKeys[addrs.IntKey(0)]
	keepKey := addrs.NoKey
	if have0Key && !haveNoKey {
		// If we don't have a no-key instance then we can use the 0-key instance
		// instead.
		keepKey = addrs.IntKey(0)
	}

	for key := range haveKeys {
		if key == keepKey {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}