github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/terraform/state_filter.go (about) 1 package terraform 2 3 import ( 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. 18 type 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. 25 func (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 62 func (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. 168 func (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. 208 type 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 226 func (r *StateFilterResult) String() string { 227 return fmt.Sprintf("%T: %s", r.Value, r.Address) 228 } 229 230 func (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. 246 type StateFilterResultSlice []*StateFilterResult 247 248 func (s StateFilterResultSlice) Len() int { return len(s) } 249 func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 250 func (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 }