github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/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  }