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