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