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