github.com/ojiry/terraform@v0.8.2-0.20161218223921-e50cec712c4a/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 (v *ModuleVariable) GoString() string { 153 return fmt.Sprintf("*%#v", *v) 154 } 155 156 func NewPathVariable(key string) (*PathVariable, error) { 157 var fieldType PathValueType 158 parts := strings.SplitN(key, ".", 2) 159 switch parts[1] { 160 case "cwd": 161 fieldType = PathValueCwd 162 case "module": 163 fieldType = PathValueModule 164 case "root": 165 fieldType = PathValueRoot 166 } 167 168 return &PathVariable{ 169 Type: fieldType, 170 key: key, 171 }, nil 172 } 173 174 func (v *PathVariable) FullKey() string { 175 return v.key 176 } 177 178 func NewResourceVariable(key string) (*ResourceVariable, error) { 179 var mode ResourceMode 180 var parts []string 181 if strings.HasPrefix(key, "data.") { 182 mode = DataResourceMode 183 parts = strings.SplitN(key, ".", 4) 184 if len(parts) < 4 { 185 return nil, fmt.Errorf( 186 "%s: data variables must be four parts: data.TYPE.NAME.ATTR", 187 key) 188 } 189 190 // Don't actually need the "data." prefix for parsing, since it's 191 // always constant. 192 parts = parts[1:] 193 } else { 194 mode = ManagedResourceMode 195 parts = strings.SplitN(key, ".", 3) 196 if len(parts) < 3 { 197 return nil, fmt.Errorf( 198 "%s: resource variables must be three parts: TYPE.NAME.ATTR", 199 key) 200 } 201 } 202 203 field := parts[2] 204 multi := false 205 var index int 206 207 if idx := strings.Index(field, "."); idx != -1 { 208 indexStr := field[:idx] 209 multi = indexStr == "*" 210 index = -1 211 212 if !multi { 213 indexInt, err := strconv.ParseInt(indexStr, 0, 0) 214 if err == nil { 215 multi = true 216 index = int(indexInt) 217 } 218 } 219 220 if multi { 221 field = field[idx+1:] 222 } 223 } 224 225 return &ResourceVariable{ 226 Mode: mode, 227 Type: parts[0], 228 Name: parts[1], 229 Field: field, 230 Multi: multi, 231 Index: index, 232 key: key, 233 }, nil 234 } 235 236 func (v *ResourceVariable) ResourceId() string { 237 switch v.Mode { 238 case ManagedResourceMode: 239 return fmt.Sprintf("%s.%s", v.Type, v.Name) 240 case DataResourceMode: 241 return fmt.Sprintf("data.%s.%s", v.Type, v.Name) 242 default: 243 panic(fmt.Errorf("unknown resource mode %s", v.Mode)) 244 } 245 } 246 247 func (v *ResourceVariable) FullKey() string { 248 return v.key 249 } 250 251 func NewSelfVariable(key string) (*SelfVariable, error) { 252 field := key[len("self."):] 253 254 return &SelfVariable{ 255 Field: field, 256 257 key: key, 258 }, nil 259 } 260 261 func (v *SelfVariable) FullKey() string { 262 return v.key 263 } 264 265 func (v *SelfVariable) GoString() string { 266 return fmt.Sprintf("*%#v", *v) 267 } 268 269 func NewSimpleVariable(key string) (*SimpleVariable, error) { 270 return &SimpleVariable{key}, nil 271 } 272 273 func (v *SimpleVariable) FullKey() string { 274 return v.Key 275 } 276 277 func (v *SimpleVariable) GoString() string { 278 return fmt.Sprintf("*%#v", *v) 279 } 280 281 func NewUserVariable(key string) (*UserVariable, error) { 282 name := key[len("var."):] 283 elem := "" 284 if idx := strings.Index(name, "."); idx > -1 { 285 elem = name[idx+1:] 286 name = name[:idx] 287 } 288 289 if len(elem) > 0 { 290 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) 291 } 292 293 return &UserVariable{ 294 key: key, 295 296 Name: name, 297 Elem: elem, 298 }, nil 299 } 300 301 func (v *UserVariable) FullKey() string { 302 return v.key 303 } 304 305 func (v *UserVariable) GoString() string { 306 return fmt.Sprintf("*%#v", *v) 307 } 308 309 // DetectVariables takes an AST root and returns all the interpolated 310 // variables that are detected in the AST tree. 311 func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { 312 var result []InterpolatedVariable 313 var resultErr error 314 315 // Visitor callback 316 fn := func(n ast.Node) ast.Node { 317 if resultErr != nil { 318 return n 319 } 320 321 switch vn := n.(type) { 322 case *ast.VariableAccess: 323 v, err := NewInterpolatedVariable(vn.Name) 324 if err != nil { 325 resultErr = err 326 return n 327 } 328 result = append(result, v) 329 case *ast.Index: 330 if va, ok := vn.Target.(*ast.VariableAccess); ok { 331 v, err := NewInterpolatedVariable(va.Name) 332 if err != nil { 333 resultErr = err 334 return n 335 } 336 result = append(result, v) 337 } 338 if va, ok := vn.Key.(*ast.VariableAccess); ok { 339 v, err := NewInterpolatedVariable(va.Name) 340 if err != nil { 341 resultErr = err 342 return n 343 } 344 result = append(result, v) 345 } 346 default: 347 return n 348 } 349 350 return n 351 } 352 353 // Visitor pattern 354 root.Accept(fn) 355 356 if resultErr != nil { 357 return nil, resultErr 358 } 359 360 return result, nil 361 }