github.com/grange74/terraform@v0.7.0-rc3.0.20160722171430-8c8803864753/config/interpolate.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/hashicorp/hil/ast" 9 ) 10 11 // An InterpolatedVariable is a variable reference within an interpolation. 12 // 13 // Implementations of this interface represents various sources where 14 // variables can come from: user variables, resources, etc. 15 type InterpolatedVariable interface { 16 FullKey() string 17 } 18 19 // CountVariable is a variable for referencing information about 20 // the count. 21 type CountVariable struct { 22 Type CountValueType 23 key string 24 } 25 26 // CountValueType is the type of the count variable that is referenced. 27 type CountValueType byte 28 29 const ( 30 CountValueInvalid CountValueType = iota 31 CountValueIndex 32 ) 33 34 // A ModuleVariable is a variable that is referencing the output 35 // of a module, such as "${module.foo.bar}" 36 type ModuleVariable struct { 37 Name string 38 Field string 39 key string 40 } 41 42 // A PathVariable is a variable that references path information about the 43 // module. 44 type PathVariable struct { 45 Type PathValueType 46 key string 47 } 48 49 type PathValueType byte 50 51 const ( 52 PathValueInvalid PathValueType = iota 53 PathValueCwd 54 PathValueModule 55 PathValueRoot 56 ) 57 58 // A ResourceVariable is a variable that is referencing the field 59 // of a resource, such as "${aws_instance.foo.ami}" 60 type ResourceVariable struct { 61 Mode ResourceMode 62 Type string // Resource type, i.e. "aws_instance" 63 Name string // Resource name 64 Field string // Resource field 65 66 Multi bool // True if multi-variable: aws_instance.foo.*.id 67 Index int // Index for multi-variable: aws_instance.foo.1.id == 1 68 69 key string 70 } 71 72 // SelfVariable is a variable that is referencing the same resource 73 // it is running on: "${self.address}" 74 type SelfVariable struct { 75 Field string 76 77 key string 78 } 79 80 // SimpleVariable is an unprefixed variable, which can show up when users have 81 // strings they are passing down to resources that use interpolation 82 // internally. The template_file resource is an example of this. 83 type SimpleVariable struct { 84 Key string 85 } 86 87 // A UserVariable is a variable that is referencing a user variable 88 // that is inputted from outside the configuration. This looks like 89 // "${var.foo}" 90 type UserVariable struct { 91 Name string 92 Elem string 93 94 key string 95 } 96 97 func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { 98 if strings.HasPrefix(v, "count.") { 99 return NewCountVariable(v) 100 } else if strings.HasPrefix(v, "path.") { 101 return NewPathVariable(v) 102 } else if strings.HasPrefix(v, "self.") { 103 return NewSelfVariable(v) 104 } else if strings.HasPrefix(v, "var.") { 105 return NewUserVariable(v) 106 } else if strings.HasPrefix(v, "module.") { 107 return NewModuleVariable(v) 108 } else if !strings.ContainsRune(v, '.') { 109 return NewSimpleVariable(v) 110 } else { 111 return NewResourceVariable(v) 112 } 113 } 114 115 func NewCountVariable(key string) (*CountVariable, error) { 116 var fieldType CountValueType 117 parts := strings.SplitN(key, ".", 2) 118 switch parts[1] { 119 case "index": 120 fieldType = CountValueIndex 121 } 122 123 return &CountVariable{ 124 Type: fieldType, 125 key: key, 126 }, nil 127 } 128 129 func (c *CountVariable) FullKey() string { 130 return c.key 131 } 132 133 func NewModuleVariable(key string) (*ModuleVariable, error) { 134 parts := strings.SplitN(key, ".", 3) 135 if len(parts) < 3 { 136 return nil, fmt.Errorf( 137 "%s: module variables must be three parts: module.name.attr", 138 key) 139 } 140 141 return &ModuleVariable{ 142 Name: parts[1], 143 Field: parts[2], 144 key: key, 145 }, nil 146 } 147 148 func (v *ModuleVariable) FullKey() string { 149 return v.key 150 } 151 152 func NewPathVariable(key string) (*PathVariable, error) { 153 var fieldType PathValueType 154 parts := strings.SplitN(key, ".", 2) 155 switch parts[1] { 156 case "cwd": 157 fieldType = PathValueCwd 158 case "module": 159 fieldType = PathValueModule 160 case "root": 161 fieldType = PathValueRoot 162 } 163 164 return &PathVariable{ 165 Type: fieldType, 166 key: key, 167 }, nil 168 } 169 170 func (v *PathVariable) FullKey() string { 171 return v.key 172 } 173 174 func NewResourceVariable(key string) (*ResourceVariable, error) { 175 var mode ResourceMode 176 var parts []string 177 if strings.HasPrefix(key, "data.") { 178 mode = DataResourceMode 179 parts = strings.SplitN(key, ".", 4) 180 if len(parts) < 4 { 181 return nil, fmt.Errorf( 182 "%s: data variables must be four parts: data.TYPE.NAME.ATTR", 183 key) 184 } 185 186 // Don't actually need the "data." prefix for parsing, since it's 187 // always constant. 188 parts = parts[1:] 189 } else { 190 mode = ManagedResourceMode 191 parts = strings.SplitN(key, ".", 3) 192 if len(parts) < 3 { 193 return nil, fmt.Errorf( 194 "%s: resource variables must be three parts: TYPE.NAME.ATTR", 195 key) 196 } 197 } 198 199 field := parts[2] 200 multi := false 201 var index int 202 203 if idx := strings.Index(field, "."); idx != -1 { 204 indexStr := field[:idx] 205 multi = indexStr == "*" 206 index = -1 207 208 if !multi { 209 indexInt, err := strconv.ParseInt(indexStr, 0, 0) 210 if err == nil { 211 multi = true 212 index = int(indexInt) 213 } 214 } 215 216 if multi { 217 field = field[idx+1:] 218 } 219 } 220 221 return &ResourceVariable{ 222 Mode: mode, 223 Type: parts[0], 224 Name: parts[1], 225 Field: field, 226 Multi: multi, 227 Index: index, 228 key: key, 229 }, nil 230 } 231 232 func (v *ResourceVariable) ResourceId() string { 233 switch v.Mode { 234 case ManagedResourceMode: 235 return fmt.Sprintf("%s.%s", v.Type, v.Name) 236 case DataResourceMode: 237 return fmt.Sprintf("data.%s.%s", v.Type, v.Name) 238 default: 239 panic(fmt.Errorf("unknown resource mode %s", v.Mode)) 240 } 241 } 242 243 func (v *ResourceVariable) FullKey() string { 244 return v.key 245 } 246 247 func NewSelfVariable(key string) (*SelfVariable, error) { 248 field := key[len("self."):] 249 250 return &SelfVariable{ 251 Field: field, 252 253 key: key, 254 }, nil 255 } 256 257 func (v *SelfVariable) FullKey() string { 258 return v.key 259 } 260 261 func (v *SelfVariable) GoString() string { 262 return fmt.Sprintf("*%#v", *v) 263 } 264 265 func NewSimpleVariable(key string) (*SimpleVariable, error) { 266 return &SimpleVariable{key}, nil 267 } 268 269 func (v *SimpleVariable) FullKey() string { 270 return v.Key 271 } 272 273 func (v *SimpleVariable) GoString() string { 274 return fmt.Sprintf("*%#v", *v) 275 } 276 277 func NewUserVariable(key string) (*UserVariable, error) { 278 name := key[len("var."):] 279 elem := "" 280 if idx := strings.Index(name, "."); idx > -1 { 281 elem = name[idx+1:] 282 name = name[:idx] 283 } 284 285 if len(elem) > 0 { 286 return nil, fmt.Errorf("Invalid dot index found: 'var.%s.%s'. Values in maps and lists can be referenced using square bracket indexing, like: 'var.mymap[\"key\"]' or 'var.mylist[1]'.", name, elem) 287 } 288 289 return &UserVariable{ 290 key: key, 291 292 Name: name, 293 Elem: elem, 294 }, nil 295 } 296 297 func (v *UserVariable) FullKey() string { 298 return v.key 299 } 300 301 func (v *UserVariable) GoString() string { 302 return fmt.Sprintf("*%#v", *v) 303 } 304 305 // DetectVariables takes an AST root and returns all the interpolated 306 // variables that are detected in the AST tree. 307 func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { 308 var result []InterpolatedVariable 309 var resultErr error 310 311 // Visitor callback 312 fn := func(n ast.Node) ast.Node { 313 if resultErr != nil { 314 return n 315 } 316 317 switch vn := n.(type) { 318 case *ast.VariableAccess: 319 v, err := NewInterpolatedVariable(vn.Name) 320 if err != nil { 321 resultErr = err 322 return n 323 } 324 result = append(result, v) 325 case *ast.Index: 326 if va, ok := vn.Target.(*ast.VariableAccess); ok { 327 v, err := NewInterpolatedVariable(va.Name) 328 if err != nil { 329 resultErr = err 330 return n 331 } 332 result = append(result, v) 333 } 334 if va, ok := vn.Key.(*ast.VariableAccess); ok { 335 v, err := NewInterpolatedVariable(va.Name) 336 if err != nil { 337 resultErr = err 338 return n 339 } 340 result = append(result, v) 341 } 342 default: 343 return n 344 } 345 346 return n 347 } 348 349 // Visitor pattern 350 root.Accept(fn) 351 352 if resultErr != nil { 353 return nil, resultErr 354 } 355 356 return result, nil 357 }