github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/terraform/interpolate.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "regexp" 8 "sort" 9 "strings" 10 "sync" 11 12 "github.com/hashicorp/hil" 13 "github.com/hashicorp/hil/ast" 14 "github.com/hashicorp/terraform/config" 15 "github.com/hashicorp/terraform/config/module" 16 ) 17 18 const ( 19 // VarEnvPrefix is the prefix of variables that are read from 20 // the environment to set variables here. 21 VarEnvPrefix = "TF_VAR_" 22 ) 23 24 // Interpolater is the structure responsible for determining the values 25 // for interpolations such as `aws_instance.foo.bar`. 26 type Interpolater struct { 27 Operation walkOperation 28 Module *module.Tree 29 State *State 30 StateLock *sync.RWMutex 31 VariableValues map[string]interface{} 32 VariableValuesLock *sync.Mutex 33 } 34 35 // InterpolationScope is the current scope of execution. This is required 36 // since some variables which are interpolated are dependent on what we're 37 // operating on and where we are. 38 type InterpolationScope struct { 39 Path []string 40 Resource *Resource 41 } 42 43 // Values returns the values for all the variables in the given map. 44 func (i *Interpolater) Values( 45 scope *InterpolationScope, 46 vars map[string]config.InterpolatedVariable) (map[string]ast.Variable, error) { 47 result := make(map[string]ast.Variable, len(vars)) 48 49 // Copy the default variables 50 if i.Module != nil && scope != nil { 51 mod := i.Module 52 if len(scope.Path) > 1 { 53 mod = i.Module.Child(scope.Path[1:]) 54 } 55 for _, v := range mod.Config().Variables { 56 // Set default variables 57 if v.Default == nil { 58 continue 59 } 60 61 n := fmt.Sprintf("var.%s", v.Name) 62 variable, err := hil.InterfaceToVariable(v.Default) 63 if err != nil { 64 return nil, fmt.Errorf("invalid default map value for %s: %v", v.Name, v.Default) 65 } 66 67 result[n] = variable 68 } 69 } 70 71 for n, rawV := range vars { 72 var err error 73 switch v := rawV.(type) { 74 case *config.CountVariable: 75 err = i.valueCountVar(scope, n, v, result) 76 case *config.ModuleVariable: 77 err = i.valueModuleVar(scope, n, v, result) 78 case *config.PathVariable: 79 err = i.valuePathVar(scope, n, v, result) 80 case *config.ResourceVariable: 81 err = i.valueResourceVar(scope, n, v, result) 82 case *config.SelfVariable: 83 err = i.valueSelfVar(scope, n, v, result) 84 case *config.SimpleVariable: 85 err = i.valueSimpleVar(scope, n, v, result) 86 case *config.UserVariable: 87 err = i.valueUserVar(scope, n, v, result) 88 default: 89 err = fmt.Errorf("%s: unknown variable type: %T", n, rawV) 90 } 91 92 if err != nil { 93 return nil, err 94 } 95 } 96 97 return result, nil 98 } 99 100 func (i *Interpolater) valueCountVar( 101 scope *InterpolationScope, 102 n string, 103 v *config.CountVariable, 104 result map[string]ast.Variable) error { 105 switch v.Type { 106 case config.CountValueIndex: 107 if scope.Resource == nil { 108 return fmt.Errorf("%s: count.index is only valid within resources", n) 109 } 110 result[n] = ast.Variable{ 111 Value: scope.Resource.CountIndex, 112 Type: ast.TypeInt, 113 } 114 return nil 115 default: 116 return fmt.Errorf("%s: unknown count type: %#v", n, v.Type) 117 } 118 } 119 120 func unknownVariable() ast.Variable { 121 return ast.Variable{ 122 Type: ast.TypeString, 123 Value: config.UnknownVariableValue, 124 } 125 } 126 127 func (i *Interpolater) valueModuleVar( 128 scope *InterpolationScope, 129 n string, 130 v *config.ModuleVariable, 131 result map[string]ast.Variable) error { 132 // If we're computing all dynamic fields, then module vars count 133 // and we mark it as computed. 134 if i.Operation == walkValidate { 135 result[n] = ast.Variable{ 136 Value: config.UnknownVariableValue, 137 Type: ast.TypeString, 138 } 139 return nil 140 } 141 142 // Build the path to the child module we want 143 path := make([]string, len(scope.Path), len(scope.Path)+1) 144 copy(path, scope.Path) 145 path = append(path, v.Name) 146 147 // Grab the lock so that if other interpolations are running or 148 // state is being modified, we'll be safe. 149 i.StateLock.RLock() 150 defer i.StateLock.RUnlock() 151 152 // Get the module where we're looking for the value 153 mod := i.State.ModuleByPath(path) 154 if mod == nil { 155 // If the module doesn't exist, then we can return an empty string. 156 // This happens usually only in Refresh() when we haven't populated 157 // a state. During validation, we semantically verify that all 158 // modules reference other modules, and graph ordering should 159 // ensure that the module is in the state, so if we reach this 160 // point otherwise it really is a panic. 161 result[n] = unknownVariable() 162 } else { 163 // Get the value from the outputs 164 if outputState, ok := mod.Outputs[v.Field]; ok { 165 output, err := hil.InterfaceToVariable(outputState.Value) 166 if err != nil { 167 return err 168 } 169 result[n] = output 170 } else { 171 // Same reasons as the comment above. 172 result[n] = unknownVariable() 173 174 } 175 } 176 177 return nil 178 } 179 180 func (i *Interpolater) valuePathVar( 181 scope *InterpolationScope, 182 n string, 183 v *config.PathVariable, 184 result map[string]ast.Variable) error { 185 switch v.Type { 186 case config.PathValueCwd: 187 wd, err := os.Getwd() 188 if err != nil { 189 return fmt.Errorf( 190 "Couldn't get cwd for var %s: %s", 191 v.FullKey(), err) 192 } 193 194 result[n] = ast.Variable{ 195 Value: wd, 196 Type: ast.TypeString, 197 } 198 case config.PathValueModule: 199 if t := i.Module.Child(scope.Path[1:]); t != nil { 200 result[n] = ast.Variable{ 201 Value: t.Config().Dir, 202 Type: ast.TypeString, 203 } 204 } 205 case config.PathValueRoot: 206 result[n] = ast.Variable{ 207 Value: i.Module.Config().Dir, 208 Type: ast.TypeString, 209 } 210 default: 211 return fmt.Errorf("%s: unknown path type: %#v", n, v.Type) 212 } 213 214 return nil 215 216 } 217 218 func (i *Interpolater) valueResourceVar( 219 scope *InterpolationScope, 220 n string, 221 v *config.ResourceVariable, 222 result map[string]ast.Variable) error { 223 // If we're computing all dynamic fields, then module vars count 224 // and we mark it as computed. 225 if i.Operation == walkValidate { 226 result[n] = ast.Variable{ 227 Value: config.UnknownVariableValue, 228 Type: ast.TypeString, 229 } 230 return nil 231 } 232 233 var variable *ast.Variable 234 var err error 235 236 if v.Multi && v.Index == -1 { 237 variable, err = i.computeResourceMultiVariable(scope, v) 238 } else { 239 variable, err = i.computeResourceVariable(scope, v) 240 } 241 242 if err != nil { 243 return err 244 } 245 246 if variable == nil { 247 // During the input walk we tolerate missing variables because 248 // we haven't yet had a chance to refresh state, so dynamic data may 249 // not yet be complete. 250 // If it truly is missing, we'll catch it on a later walk. 251 // This applies only to graph nodes that interpolate during the 252 // config walk, e.g. providers. 253 if i.Operation == walkInput { 254 result[n] = ast.Variable{ 255 Value: config.UnknownVariableValue, 256 Type: ast.TypeString, 257 } 258 return nil 259 } 260 261 return fmt.Errorf("variable %q is nil, but no error was reported", v.Name) 262 } 263 264 result[n] = *variable 265 return nil 266 } 267 268 func (i *Interpolater) valueSelfVar( 269 scope *InterpolationScope, 270 n string, 271 v *config.SelfVariable, 272 result map[string]ast.Variable) error { 273 if scope == nil || scope.Resource == nil { 274 return fmt.Errorf( 275 "%s: invalid scope, self variables are only valid on resources", n) 276 } 277 rv, err := config.NewResourceVariable(fmt.Sprintf( 278 "%s.%s.%d.%s", 279 scope.Resource.Type, 280 scope.Resource.Name, 281 scope.Resource.CountIndex, 282 v.Field)) 283 if err != nil { 284 return err 285 } 286 287 return i.valueResourceVar(scope, n, rv, result) 288 } 289 290 func (i *Interpolater) valueSimpleVar( 291 scope *InterpolationScope, 292 n string, 293 v *config.SimpleVariable, 294 result map[string]ast.Variable) error { 295 // SimpleVars are never handled by Terraform's interpolator 296 result[n] = ast.Variable{ 297 Value: config.UnknownVariableValue, 298 Type: ast.TypeString, 299 } 300 return nil 301 } 302 303 func (i *Interpolater) valueUserVar( 304 scope *InterpolationScope, 305 n string, 306 v *config.UserVariable, 307 result map[string]ast.Variable) error { 308 i.VariableValuesLock.Lock() 309 defer i.VariableValuesLock.Unlock() 310 val, ok := i.VariableValues[v.Name] 311 if ok { 312 varValue, err := hil.InterfaceToVariable(val) 313 if err != nil { 314 return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s", 315 v.Name, val, err) 316 } 317 result[n] = varValue 318 return nil 319 } 320 321 if _, ok := result[n]; !ok && i.Operation == walkValidate { 322 result[n] = unknownVariable() 323 return nil 324 } 325 326 // Look up if we have any variables with this prefix because 327 // those are map overrides. Include those. 328 for k, val := range i.VariableValues { 329 if strings.HasPrefix(k, v.Name+".") { 330 keyComponents := strings.Split(k, ".") 331 overrideKey := keyComponents[len(keyComponents)-1] 332 333 mapInterface, ok := result["var."+v.Name] 334 if !ok { 335 return fmt.Errorf("override for non-existent variable: %s", v.Name) 336 } 337 338 mapVariable := mapInterface.Value.(map[string]ast.Variable) 339 340 varValue, err := hil.InterfaceToVariable(val) 341 if err != nil { 342 return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s", 343 v.Name, val, err) 344 } 345 mapVariable[overrideKey] = varValue 346 } 347 } 348 349 return nil 350 } 351 352 func (i *Interpolater) computeResourceVariable( 353 scope *InterpolationScope, 354 v *config.ResourceVariable) (*ast.Variable, error) { 355 id := v.ResourceId() 356 if v.Multi { 357 id = fmt.Sprintf("%s.%d", id, v.Index) 358 } 359 360 i.StateLock.RLock() 361 defer i.StateLock.RUnlock() 362 363 unknownVariable := unknownVariable() 364 365 // Get the information about this resource variable, and verify 366 // that it exists and such. 367 module, _, err := i.resourceVariableInfo(scope, v) 368 if err != nil { 369 return nil, err 370 } 371 372 // If we have no module in the state yet or count, return empty 373 if module == nil || len(module.Resources) == 0 { 374 return nil, nil 375 } 376 377 // Get the resource out from the state. We know the state exists 378 // at this point and if there is a state, we expect there to be a 379 // resource with the given name. 380 r, ok := module.Resources[id] 381 if !ok && v.Multi && v.Index == 0 { 382 r, ok = module.Resources[v.ResourceId()] 383 } 384 if !ok { 385 r = nil 386 } 387 if r == nil { 388 goto MISSING 389 } 390 391 if r.Primary == nil { 392 goto MISSING 393 } 394 395 if attr, ok := r.Primary.Attributes[v.Field]; ok { 396 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 397 } 398 399 // computed list or map attribute 400 if _, ok := r.Primary.Attributes[v.Field+".#"]; ok { 401 variable, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes) 402 return &variable, err 403 } 404 405 // At apply time, we can't do the "maybe has it" check below 406 // that we need for plans since parent elements might be computed. 407 // Therefore, it is an error and we're missing the key. 408 // 409 // TODO: test by creating a state and configuration that is referencing 410 // a non-existent variable "foo.bar" where the state only has "foo" 411 // and verify plan works, but apply doesn't. 412 if i.Operation == walkApply || i.Operation == walkDestroy { 413 goto MISSING 414 } 415 416 // We didn't find the exact field, so lets separate the dots 417 // and see if anything along the way is a computed set. i.e. if 418 // we have "foo.0.bar" as the field, check to see if "foo" is 419 // a computed list. If so, then the whole thing is computed. 420 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 421 for i := 1; i < len(parts); i++ { 422 // Lists and sets make this 423 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 424 if attr, ok := r.Primary.Attributes[key]; ok { 425 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 426 } 427 428 // Maps make this 429 key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) 430 if attr, ok := r.Primary.Attributes[key]; ok { 431 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 432 } 433 } 434 } 435 436 MISSING: 437 // Validation for missing interpolations should happen at a higher 438 // semantic level. If we reached this point and don't have variables, 439 // just return the computed value. 440 if scope == nil && scope.Resource == nil { 441 return &unknownVariable, nil 442 } 443 444 // If the operation is refresh, it isn't an error for a value to 445 // be unknown. Instead, we return that the value is computed so 446 // that the graph can continue to refresh other nodes. It doesn't 447 // matter because the config isn't interpolated anyways. 448 // 449 // For a Destroy, we're also fine with computed values, since our goal is 450 // only to get destroy nodes for existing resources. 451 // 452 // For an input walk, computed values are okay to return because we're only 453 // looking for missing variables to prompt the user for. 454 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 455 return &unknownVariable, nil 456 } 457 458 return nil, fmt.Errorf( 459 "Resource '%s' does not have attribute '%s' "+ 460 "for variable '%s'", 461 id, 462 v.Field, 463 v.FullKey()) 464 } 465 466 func (i *Interpolater) computeResourceMultiVariable( 467 scope *InterpolationScope, 468 v *config.ResourceVariable) (*ast.Variable, error) { 469 i.StateLock.RLock() 470 defer i.StateLock.RUnlock() 471 472 unknownVariable := unknownVariable() 473 474 // Get the information about this resource variable, and verify 475 // that it exists and such. 476 module, cr, err := i.resourceVariableInfo(scope, v) 477 if err != nil { 478 return nil, err 479 } 480 481 // Get the count so we know how many to iterate over 482 count, err := cr.Count() 483 if err != nil { 484 return nil, fmt.Errorf( 485 "Error reading %s count: %s", 486 v.ResourceId(), 487 err) 488 } 489 490 // If we have no module in the state yet or count, return empty 491 if module == nil || len(module.Resources) == 0 || count == 0 { 492 return &ast.Variable{Type: ast.TypeString, Value: ""}, nil 493 } 494 495 var values []string 496 for j := 0; j < count; j++ { 497 id := fmt.Sprintf("%s.%d", v.ResourceId(), j) 498 499 // If we're dealing with only a single resource, then the 500 // ID doesn't have a trailing index. 501 if count == 1 { 502 id = v.ResourceId() 503 } 504 505 r, ok := module.Resources[id] 506 if !ok { 507 continue 508 } 509 510 if r.Primary == nil { 511 continue 512 } 513 514 if singleAttr, ok := r.Primary.Attributes[v.Field]; ok { 515 if singleAttr == config.UnknownVariableValue { 516 return &unknownVariable, nil 517 } 518 519 values = append(values, singleAttr) 520 continue 521 } 522 523 // computed list attribute 524 _, ok = r.Primary.Attributes[v.Field+".#"] 525 if !ok { 526 continue 527 } 528 multiAttr, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes) 529 if err != nil { 530 return nil, err 531 } 532 533 if multiAttr == unknownVariable { 534 return &ast.Variable{Type: ast.TypeString, Value: ""}, nil 535 } 536 537 for _, element := range multiAttr.Value.([]ast.Variable) { 538 strVal := element.Value.(string) 539 if strVal == config.UnknownVariableValue { 540 return &unknownVariable, nil 541 } 542 543 values = append(values, strVal) 544 } 545 } 546 547 if len(values) == 0 { 548 // If the operation is refresh, it isn't an error for a value to 549 // be unknown. Instead, we return that the value is computed so 550 // that the graph can continue to refresh other nodes. It doesn't 551 // matter because the config isn't interpolated anyways. 552 // 553 // For a Destroy, we're also fine with computed values, since our goal is 554 // only to get destroy nodes for existing resources. 555 // 556 // For an input walk, computed values are okay to return because we're only 557 // looking for missing variables to prompt the user for. 558 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 559 return &unknownVariable, nil 560 } 561 562 return nil, fmt.Errorf( 563 "Resource '%s' does not have attribute '%s' "+ 564 "for variable '%s'", 565 v.ResourceId(), 566 v.Field, 567 v.FullKey()) 568 } 569 570 variable, err := hil.InterfaceToVariable(values) 571 return &variable, err 572 } 573 574 func (i *Interpolater) interpolateComplexTypeAttribute( 575 resourceID string, 576 attributes map[string]string) (ast.Variable, error) { 577 578 attr := attributes[resourceID+".#"] 579 log.Printf("[DEBUG] Interpolating computed complex type attribute %s (%s)", 580 resourceID, attr) 581 582 // In Terraform's internal dotted representation of list-like attributes, the 583 // ".#" count field is marked as unknown to indicate "this whole list is 584 // unknown". We must honor that meaning here so computed references can be 585 // treated properly during the plan phase. 586 if attr == config.UnknownVariableValue { 587 return unknownVariable(), nil 588 } 589 590 // At this stage we don't know whether the item is a list or a map, so we 591 // examine the keys to see whether they are all numeric. 592 var numericKeys []string 593 var allKeys []string 594 numberedListKey := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$") 595 otherListKey := regexp.MustCompile("^" + resourceID + "\\.([^#]+)$") 596 for id, _ := range attributes { 597 if numberedListKey.MatchString(id) { 598 numericKeys = append(numericKeys, id) 599 } 600 if submatches := otherListKey.FindAllStringSubmatch(id, -1); len(submatches) > 0 { 601 allKeys = append(allKeys, submatches[0][1]) 602 } 603 } 604 605 if len(numericKeys) == len(allKeys) { 606 // This is a list 607 var members []string 608 for _, key := range numericKeys { 609 members = append(members, attributes[key]) 610 } 611 sort.Strings(members) 612 return hil.InterfaceToVariable(members) 613 } else { 614 // This is a map 615 members := make(map[string]interface{}) 616 for _, key := range allKeys { 617 members[key] = attributes[resourceID+"."+key] 618 } 619 return hil.InterfaceToVariable(members) 620 } 621 } 622 623 func (i *Interpolater) resourceVariableInfo( 624 scope *InterpolationScope, 625 v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { 626 // Get the module tree that contains our current path. This is 627 // either the current module (path is empty) or a child. 628 modTree := i.Module 629 if len(scope.Path) > 1 { 630 modTree = i.Module.Child(scope.Path[1:]) 631 } 632 633 // Get the resource from the configuration so we can verify 634 // that the resource is in the configuration and so we can access 635 // the configuration if we need to. 636 var cr *config.Resource 637 for _, r := range modTree.Config().Resources { 638 if r.Id() == v.ResourceId() { 639 cr = r 640 break 641 } 642 } 643 if cr == nil { 644 return nil, nil, fmt.Errorf( 645 "Resource '%s' not found for variable '%s'", 646 v.ResourceId(), 647 v.FullKey()) 648 } 649 650 // Get the relevant module 651 module := i.State.ModuleByPath(scope.Path) 652 return module, cr, nil 653 }