]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/state_filter.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / state_filter.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "fmt"
5 "sort"
6)
7
8// StateFilter is responsible for filtering and searching a state.
9//
10// This is a separate struct from State rather than a method on State
11// because StateFilter might create sidecar data structures to optimize
12// filtering on the state.
13//
14// If you change the State, the filter created is invalid and either
15// Reset should be called or a new one should be allocated. StateFilter
16// will not watch State for changes and do this for you. If you filter after
17// changing the State without calling Reset, the behavior is not defined.
18type StateFilter struct {
19 State *State
20}
21
22// Filter takes the addresses specified by fs and finds all the matches.
23// The values of fs are resource addressing syntax that can be parsed by
24// ParseResourceAddress.
25func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) {
26 // Parse all the addresses
27 as := make([]*ResourceAddress, len(fs))
28 for i, v := range fs {
29 a, err := ParseResourceAddress(v)
30 if err != nil {
31 return nil, fmt.Errorf("Error parsing address '%s': %s", v, err)
32 }
33
34 as[i] = a
35 }
36
37 // If we weren't given any filters, then we list all
38 if len(fs) == 0 {
39 as = append(as, &ResourceAddress{Index: -1})
40 }
41
42 // Filter each of the address. We keep track of this in a map to
43 // strip duplicates.
44 resultSet := make(map[string]*StateFilterResult)
45 for _, a := range as {
46 for _, r := range f.filterSingle(a) {
47 resultSet[r.String()] = r
48 }
49 }
50
51 // Make the result list
52 results := make([]*StateFilterResult, 0, len(resultSet))
53 for _, v := range resultSet {
54 results = append(results, v)
55 }
56
57 // Sort them and return
58 sort.Sort(StateFilterResultSlice(results))
59 return results, nil
60}
61
62func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult {
63 // The slice to keep track of results
64 var results []*StateFilterResult
65
66 // Go through modules first.
67 modules := make([]*ModuleState, 0, len(f.State.Modules))
68 for _, m := range f.State.Modules {
69 if f.relevant(a, m) {
70 modules = append(modules, m)
71
72 // Only add the module to the results if we haven't specified a type.
73 // We also ignore the root module.
74 if a.Type == "" && len(m.Path) > 1 {
75 results = append(results, &StateFilterResult{
76 Path: m.Path[1:],
77 Address: (&ResourceAddress{Path: m.Path[1:]}).String(),
78 Value: m,
79 })
80 }
81 }
82 }
83
84 // With the modules set, go through all the resources within
85 // the modules to find relevant resources.
86 for _, m := range modules {
87 for n, r := range m.Resources {
88 // The name in the state contains valuable information. Parse.
89 key, err := ParseResourceStateKey(n)
90 if err != nil {
91 // If we get an error parsing, then just ignore it
92 // out of the state.
93 continue
94 }
95
96 // Older states and test fixtures often don't contain the
97 // type directly on the ResourceState. We add this so StateFilter
98 // is a bit more robust.
99 if r.Type == "" {
100 r.Type = key.Type
101 }
102
103 if f.relevant(a, r) {
104 if a.Name != "" && a.Name != key.Name {
105 // Name doesn't match
106 continue
107 }
108
109 if a.Index >= 0 && key.Index != a.Index {
110 // Index doesn't match
111 continue
112 }
113
114 if a.Name != "" && a.Name != key.Name {
115 continue
116 }
117
118 // Build the address for this resource
119 addr := &ResourceAddress{
120 Path: m.Path[1:],
121 Name: key.Name,
122 Type: key.Type,
123 Index: key.Index,
124 }
125
126 // Add the resource level result
127 resourceResult := &StateFilterResult{
128 Path: addr.Path,
129 Address: addr.String(),
130 Value: r,
131 }
132 if !a.InstanceTypeSet {
133 results = append(results, resourceResult)
134 }
135
136 // Add the instances
137 if r.Primary != nil {
138 addr.InstanceType = TypePrimary
139 addr.InstanceTypeSet = false
140 results = append(results, &StateFilterResult{
141 Path: addr.Path,
142 Address: addr.String(),
143 Parent: resourceResult,
144 Value: r.Primary,
145 })
146 }
147
148 for _, instance := range r.Deposed {
149 if f.relevant(a, instance) {
150 addr.InstanceType = TypeDeposed
151 addr.InstanceTypeSet = true
152 results = append(results, &StateFilterResult{
153 Path: addr.Path,
154 Address: addr.String(),
155 Parent: resourceResult,
156 Value: instance,
157 })
158 }
159 }
160 }
161 }
162 }
163
164 return results
165}
166
167// relevant checks for relevance of this address against the given value.
168func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool {
169 switch v := raw.(type) {
170 case *ModuleState:
171 path := v.Path[1:]
172
173 if len(addr.Path) > len(path) {
174 // Longer path in address means there is no way we match.
175 return false
176 }
177
178 // Check for a prefix match
179 for i, p := range addr.Path {
180 if path[i] != p {
181 // Any mismatches don't match.
182 return false
183 }
184 }
185
186 return true
187 case *ResourceState:
188 if addr.Type == "" {
189 // If we have no resource type, then we're interested in all!
190 return true
191 }
192
193 // If the type doesn't match we fail immediately
194 if v.Type != addr.Type {
195 return false
196 }
197
198 return true
199 default:
200 // If we don't know about it, let's just say no
201 return false
202 }
203}
204
205// StateFilterResult is a single result from a filter operation. Filter
206// can match multiple things within a state (module, resource, instance, etc.)
207// and this unifies that.
208type StateFilterResult struct {
209 // Module path of the result
210 Path []string
211
212 // Address is the address that can be used to reference this exact result.
213 Address string
214
215 // Parent, if non-nil, is a parent of this result. For instances, the
216 // parent would be a resource. For resources, the parent would be
217 // a module. For modules, this is currently nil.
218 Parent *StateFilterResult
219
220 // Value is the actual value. This must be type switched on. It can be
221 // any data structures that `State` can hold: `ModuleState`,
222 // `ResourceState`, `InstanceState`.
223 Value interface{}
224}
225
226func (r *StateFilterResult) String() string {
227 return fmt.Sprintf("%T: %s", r.Value, r.Address)
228}
229
230func (r *StateFilterResult) sortedType() int {
231 switch r.Value.(type) {
232 case *ModuleState:
233 return 0
234 case *ResourceState:
235 return 1
236 case *InstanceState:
237 return 2
238 default:
239 return 50
240 }
241}
242
243// StateFilterResultSlice is a slice of results that implements
244// sort.Interface. The sorting goal is what is most appealing to
245// human output.
246type StateFilterResultSlice []*StateFilterResult
247
248func (s StateFilterResultSlice) Len() int { return len(s) }
249func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
250func (s StateFilterResultSlice) Less(i, j int) bool {
251 a, b := s[i], s[j]
252
253 // if these address contain an index, we want to sort by index rather than name
254 addrA, errA := ParseResourceAddress(a.Address)
255 addrB, errB := ParseResourceAddress(b.Address)
256 if errA == nil && errB == nil && addrA.Name == addrB.Name && addrA.Index != addrB.Index {
257 return addrA.Index < addrB.Index
258 }
259
260 // If the addresses are different it is just lexographic sorting
261 if a.Address != b.Address {
262 return a.Address < b.Address
263 }
264
265 // Addresses are the same, which means it matters on the type
266 return a.sortedType() < b.sortedType()
267}