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 }