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