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 }