github.com/odise/terraform@v0.6.9-0.20160401223921-f7d1e0390da7/terraform/resource_address.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // ResourceAddress is a way of identifying an individual resource (or,
    12  // eventually, a subset of resources) within the state. It is used for Targets.
    13  type ResourceAddress struct {
    14  	// Addresses a resource falling somewhere in the module path
    15  	// When specified alone, addresses all resources within a module path
    16  	Path []string
    17  
    18  	// Addresses a specific resource that occurs in a list
    19  	Index int
    20  
    21  	InstanceType InstanceType
    22  	Name         string
    23  	Type         string
    24  }
    25  
    26  // Copy returns a copy of this ResourceAddress
    27  func (r *ResourceAddress) Copy() *ResourceAddress {
    28  	n := &ResourceAddress{
    29  		Path:         make([]string, 0, len(r.Path)),
    30  		Index:        r.Index,
    31  		InstanceType: r.InstanceType,
    32  		Name:         r.Name,
    33  		Type:         r.Type,
    34  	}
    35  	for _, p := range r.Path {
    36  		n.Path = append(n.Path, p)
    37  	}
    38  	return n
    39  }
    40  
    41  func ParseResourceAddress(s string) (*ResourceAddress, error) {
    42  	matches, err := tokenizeResourceAddress(s)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	resourceIndex, err := ParseResourceIndex(matches["index"])
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	instanceType, err := ParseInstanceType(matches["instance_type"])
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	path := ParseResourcePath(matches["path"])
    55  
    56  	return &ResourceAddress{
    57  		Path:         path,
    58  		Index:        resourceIndex,
    59  		InstanceType: instanceType,
    60  		Name:         matches["name"],
    61  		Type:         matches["type"],
    62  	}, nil
    63  }
    64  
    65  func (addr *ResourceAddress) Equals(raw interface{}) bool {
    66  	other, ok := raw.(*ResourceAddress)
    67  	if !ok {
    68  		return false
    69  	}
    70  
    71  	pathMatch := len(addr.Path) == 0 && len(other.Path) == 0 ||
    72  		reflect.DeepEqual(addr.Path, other.Path)
    73  
    74  	indexMatch := addr.Index == -1 ||
    75  		other.Index == -1 ||
    76  		addr.Index == other.Index
    77  
    78  	nameMatch := addr.Name == "" ||
    79  		other.Name == "" ||
    80  		addr.Name == other.Name
    81  
    82  	typeMatch := addr.Type == "" ||
    83  		other.Type == "" ||
    84  		addr.Type == other.Type
    85  
    86  	return pathMatch &&
    87  		indexMatch &&
    88  		addr.InstanceType == other.InstanceType &&
    89  		nameMatch &&
    90  		typeMatch
    91  }
    92  
    93  func ParseResourceIndex(s string) (int, error) {
    94  	if s == "" {
    95  		return -1, nil
    96  	}
    97  	return strconv.Atoi(s)
    98  }
    99  
   100  func ParseResourcePath(s string) []string {
   101  	if s == "" {
   102  		return nil
   103  	}
   104  	parts := strings.Split(s, ".")
   105  	path := make([]string, 0, len(parts))
   106  	for _, s := range parts {
   107  		// Due to the limitations of the regexp match below, the path match has
   108  		// some noise in it we have to filter out :|
   109  		if s == "" || s == "module" {
   110  			continue
   111  		}
   112  		path = append(path, s)
   113  	}
   114  	return path
   115  }
   116  
   117  func ParseInstanceType(s string) (InstanceType, error) {
   118  	switch s {
   119  	case "", "primary":
   120  		return TypePrimary, nil
   121  	case "deposed":
   122  		return TypeDeposed, nil
   123  	case "tainted":
   124  		return TypeTainted, nil
   125  	default:
   126  		return TypeInvalid, fmt.Errorf("Unexpected value for InstanceType field: %q", s)
   127  	}
   128  }
   129  
   130  func tokenizeResourceAddress(s string) (map[string]string, error) {
   131  	// Example of portions of the regexp below using the
   132  	// string "aws_instance.web.tainted[1]"
   133  	re := regexp.MustCompile(`\A` +
   134  		// "module.foo.module.bar" (optional)
   135  		`(?P<path>(?:module\.[^.]+\.?)*)` +
   136  		// "aws_instance.web" (optional when module path specified)
   137  		`(?:(?P<type>[^.]+)\.(?P<name>[^.[]+))?` +
   138  		// "tainted" (optional, omission implies: "primary")
   139  		`(?:\.(?P<instance_type>\w+))?` +
   140  		// "1" (optional, omission implies: "0")
   141  		`(?:\[(?P<index>\d+)\])?` +
   142  		`\z`)
   143  	groupNames := re.SubexpNames()
   144  	rawMatches := re.FindAllStringSubmatch(s, -1)
   145  	if len(rawMatches) != 1 {
   146  		return nil, fmt.Errorf("Problem parsing address: %q", s)
   147  	}
   148  	matches := make(map[string]string)
   149  	for i, m := range rawMatches[0] {
   150  		matches[groupNames[i]] = m
   151  	}
   152  	return matches, nil
   153  }