github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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  func ParseResourceAddress(s string) (*ResourceAddress, error) {
    27  	matches, err := tokenizeResourceAddress(s)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	resourceIndex, err := ParseResourceIndex(matches["index"])
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	instanceType, err := ParseInstanceType(matches["instance_type"])
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	path := ParseResourcePath(matches["path"])
    40  
    41  	return &ResourceAddress{
    42  		Path:         path,
    43  		Index:        resourceIndex,
    44  		InstanceType: instanceType,
    45  		Name:         matches["name"],
    46  		Type:         matches["type"],
    47  	}, nil
    48  }
    49  
    50  func (addr *ResourceAddress) Equals(raw interface{}) bool {
    51  	other, ok := raw.(*ResourceAddress)
    52  	if !ok {
    53  		return false
    54  	}
    55  
    56  	pathMatch := len(addr.Path) == 0 && len(other.Path) == 0 ||
    57  		reflect.DeepEqual(addr.Path, other.Path)
    58  
    59  	indexMatch := addr.Index == -1 ||
    60  		other.Index == -1 ||
    61  		addr.Index == other.Index
    62  
    63  	nameMatch := addr.Name == "" ||
    64  		other.Name == "" ||
    65  		addr.Name == other.Name
    66  
    67  	typeMatch := addr.Type == "" ||
    68  		other.Type == "" ||
    69  		addr.Type == other.Type
    70  
    71  	return pathMatch &&
    72  		indexMatch &&
    73  		addr.InstanceType == other.InstanceType &&
    74  		nameMatch &&
    75  		typeMatch
    76  }
    77  
    78  func ParseResourceIndex(s string) (int, error) {
    79  	if s == "" {
    80  		return -1, nil
    81  	}
    82  	return strconv.Atoi(s)
    83  }
    84  
    85  func ParseResourcePath(s string) []string {
    86  	if s == "" {
    87  		return nil
    88  	}
    89  	parts := strings.Split(s, ".")
    90  	path := make([]string, 0, len(parts))
    91  	for _, s := range parts {
    92  		// Due to the limitations of the regexp match below, the path match has
    93  		// some noise in it we have to filter out :|
    94  		if s == "" || s == "module" {
    95  			continue
    96  		}
    97  		path = append(path, s)
    98  	}
    99  	return path
   100  }
   101  
   102  func ParseInstanceType(s string) (InstanceType, error) {
   103  	switch s {
   104  	case "", "primary":
   105  		return TypePrimary, nil
   106  	case "deposed":
   107  		return TypeDeposed, nil
   108  	case "tainted":
   109  		return TypeTainted, nil
   110  	default:
   111  		return TypeInvalid, fmt.Errorf("Unexpected value for InstanceType field: %q", s)
   112  	}
   113  }
   114  
   115  func tokenizeResourceAddress(s string) (map[string]string, error) {
   116  	// Example of portions of the regexp below using the
   117  	// string "aws_instance.web.tainted[1]"
   118  	re := regexp.MustCompile(`\A` +
   119  		// "module.foo.module.bar" (optional)
   120  		`(?P<path>(?:module\.[^.]+\.?)*)` +
   121  		// "aws_instance.web" (optional when module path specified)
   122  		`(?:(?P<type>[^.]+)\.(?P<name>[^.[]+))?` +
   123  		// "tainted" (optional, omission implies: "primary")
   124  		`(?:\.(?P<instance_type>\w+))?` +
   125  		// "1" (optional, omission implies: "0")
   126  		`(?:\[(?P<index>\d+)\])?` +
   127  		`\z`)
   128  	groupNames := re.SubexpNames()
   129  	rawMatches := re.FindAllStringSubmatch(s, -1)
   130  	if len(rawMatches) != 1 {
   131  		return nil, fmt.Errorf("Problem parsing address: %q", s)
   132  	}
   133  	matches := make(map[string]string)
   134  	for i, m := range rawMatches[0] {
   135  		matches[groupNames[i]] = m
   136  	}
   137  	return matches, nil
   138  }