github.com/sarguru/terraform@v0.6.17-0.20160525232901-8fcdfd7e3dc9/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 133 // Build the path to the child module we want 134 path := make([]string, len(scope.Path), len(scope.Path)+1) 135 copy(path, scope.Path) 136 path = append(path, v.Name) 137 138 // Grab the lock so that if other interpolations are running or 139 // state is being modified, we'll be safe. 140 i.StateLock.RLock() 141 defer i.StateLock.RUnlock() 142 143 // Get the module where we're looking for the value 144 mod := i.State.ModuleByPath(path) 145 if mod == nil { 146 // If the module doesn't exist, then we can return an empty string. 147 // This happens usually only in Refresh() when we haven't populated 148 // a state. During validation, we semantically verify that all 149 // modules reference other modules, and graph ordering should 150 // ensure that the module is in the state, so if we reach this 151 // point otherwise it really is a panic. 152 result[n] = unknownVariable() 153 } else { 154 // Get the value from the outputs 155 if outputState, ok := mod.Outputs[v.Field]; ok { 156 output, err := hil.InterfaceToVariable(outputState.Value) 157 if err != nil { 158 return err 159 } 160 result[n] = output 161 } else { 162 // Same reasons as the comment above. 163 result[n] = unknownVariable() 164 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 rv, err := config.NewResourceVariable(fmt.Sprintf( 269 "%s.%s.%d.%s", 270 scope.Resource.Type, 271 scope.Resource.Name, 272 scope.Resource.CountIndex, 273 v.Field)) 274 if err != nil { 275 return err 276 } 277 278 return i.valueResourceVar(scope, n, rv, result) 279 } 280 281 func (i *Interpolater) valueSimpleVar( 282 scope *InterpolationScope, 283 n string, 284 v *config.SimpleVariable, 285 result map[string]ast.Variable) error { 286 // SimpleVars are never handled by Terraform's interpolator 287 result[n] = ast.Variable{ 288 Value: config.UnknownVariableValue, 289 Type: ast.TypeString, 290 } 291 return nil 292 } 293 294 func (i *Interpolater) valueUserVar( 295 scope *InterpolationScope, 296 n string, 297 v *config.UserVariable, 298 result map[string]ast.Variable) error { 299 i.VariableValuesLock.Lock() 300 defer i.VariableValuesLock.Unlock() 301 val, ok := i.VariableValues[v.Name] 302 if ok { 303 varValue, err := hil.InterfaceToVariable(val) 304 if err != nil { 305 return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s", 306 v.Name, val, err) 307 } 308 result[n] = varValue 309 return nil 310 } 311 312 if _, ok := result[n]; !ok && i.Operation == walkValidate { 313 result[n] = unknownVariable() 314 return nil 315 } 316 317 // Look up if we have any variables with this prefix because 318 // those are map overrides. Include those. 319 for k, val := range i.VariableValues { 320 if strings.HasPrefix(k, v.Name+".") { 321 keyComponents := strings.Split(k, ".") 322 overrideKey := keyComponents[len(keyComponents)-1] 323 324 mapInterface, ok := result["var."+v.Name] 325 if !ok { 326 return fmt.Errorf("override for non-existent variable: %s", v.Name) 327 } 328 329 mapVariable := mapInterface.Value.(map[string]ast.Variable) 330 331 varValue, err := hil.InterfaceToVariable(val) 332 if err != nil { 333 return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s", 334 v.Name, val, err) 335 } 336 mapVariable[overrideKey] = varValue 337 } 338 } 339 340 return nil 341 } 342 343 func (i *Interpolater) computeResourceVariable( 344 scope *InterpolationScope, 345 v *config.ResourceVariable) (*ast.Variable, error) { 346 id := v.ResourceId() 347 if v.Multi { 348 id = fmt.Sprintf("%s.%d", id, v.Index) 349 } 350 351 i.StateLock.RLock() 352 defer i.StateLock.RUnlock() 353 354 unknownVariable := unknownVariable() 355 356 // Get the information about this resource variable, and verify 357 // that it exists and such. 358 module, _, err := i.resourceVariableInfo(scope, v) 359 if err != nil { 360 return nil, err 361 } 362 363 // If we have no module in the state yet or count, return empty 364 if module == nil || len(module.Resources) == 0 { 365 return nil, nil 366 } 367 368 // Get the resource out from the state. We know the state exists 369 // at this point and if there is a state, we expect there to be a 370 // resource with the given name. 371 r, ok := module.Resources[id] 372 if !ok && v.Multi && v.Index == 0 { 373 r, ok = module.Resources[v.ResourceId()] 374 } 375 if !ok { 376 r = nil 377 } 378 if r == nil { 379 goto MISSING 380 } 381 382 if r.Primary == nil { 383 goto MISSING 384 } 385 386 if attr, ok := r.Primary.Attributes[v.Field]; ok { 387 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 388 } 389 390 // computed list or map attribute 391 if _, ok := r.Primary.Attributes[v.Field+".#"]; ok { 392 variable, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes) 393 return &variable, err 394 } 395 396 // At apply time, we can't do the "maybe has it" check below 397 // that we need for plans since parent elements might be computed. 398 // Therefore, it is an error and we're missing the key. 399 // 400 // TODO: test by creating a state and configuration that is referencing 401 // a non-existent variable "foo.bar" where the state only has "foo" 402 // and verify plan works, but apply doesn't. 403 if i.Operation == walkApply || i.Operation == walkDestroy { 404 goto MISSING 405 } 406 407 // We didn't find the exact field, so lets separate the dots 408 // and see if anything along the way is a computed set. i.e. if 409 // we have "foo.0.bar" as the field, check to see if "foo" is 410 // a computed list. If so, then the whole thing is computed. 411 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 412 for i := 1; i < len(parts); i++ { 413 // Lists and sets make this 414 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 415 if attr, ok := r.Primary.Attributes[key]; ok { 416 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 417 } 418 419 // Maps make this 420 key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) 421 if attr, ok := r.Primary.Attributes[key]; ok { 422 return &ast.Variable{Type: ast.TypeString, Value: attr}, nil 423 } 424 } 425 } 426 427 MISSING: 428 // Validation for missing interpolations should happen at a higher 429 // semantic level. If we reached this point and don't have variables, 430 // just return the computed value. 431 if scope == nil && scope.Resource == nil { 432 return &unknownVariable, nil 433 } 434 435 // If the operation is refresh, it isn't an error for a value to 436 // be unknown. Instead, we return that the value is computed so 437 // that the graph can continue to refresh other nodes. It doesn't 438 // matter because the config isn't interpolated anyways. 439 // 440 // For a Destroy, we're also fine with computed values, since our goal is 441 // only to get destroy nodes for existing resources. 442 // 443 // For an input walk, computed values are okay to return because we're only 444 // looking for missing variables to prompt the user for. 445 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 446 return &unknownVariable, nil 447 } 448 449 return nil, fmt.Errorf( 450 "Resource '%s' does not have attribute '%s' "+ 451 "for variable '%s'", 452 id, 453 v.Field, 454 v.FullKey()) 455 } 456 457 func (i *Interpolater) computeResourceMultiVariable( 458 scope *InterpolationScope, 459 v *config.ResourceVariable) (*ast.Variable, error) { 460 i.StateLock.RLock() 461 defer i.StateLock.RUnlock() 462 463 unknownVariable := unknownVariable() 464 465 // Get the information about this resource variable, and verify 466 // that it exists and such. 467 module, cr, err := i.resourceVariableInfo(scope, v) 468 if err != nil { 469 return nil, err 470 } 471 472 // Get the count so we know how many to iterate over 473 count, err := cr.Count() 474 if err != nil { 475 return nil, fmt.Errorf( 476 "Error reading %s count: %s", 477 v.ResourceId(), 478 err) 479 } 480 481 // If we have no module in the state yet or count, return empty 482 if module == nil || len(module.Resources) == 0 || count == 0 { 483 return &ast.Variable{Type: ast.TypeString, Value: ""}, nil 484 } 485 486 var values []string 487 for j := 0; j < count; j++ { 488 id := fmt.Sprintf("%s.%d", v.ResourceId(), j) 489 490 // If we're dealing with only a single resource, then the 491 // ID doesn't have a trailing index. 492 if count == 1 { 493 id = v.ResourceId() 494 } 495 496 r, ok := module.Resources[id] 497 if !ok { 498 continue 499 } 500 501 if r.Primary == nil { 502 continue 503 } 504 505 if singleAttr, ok := r.Primary.Attributes[v.Field]; ok { 506 if singleAttr == config.UnknownVariableValue { 507 return &unknownVariable, nil 508 } 509 510 values = append(values, singleAttr) 511 continue 512 } 513 514 // computed list attribute 515 _, ok = r.Primary.Attributes[v.Field+".#"] 516 if !ok { 517 continue 518 } 519 multiAttr, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes) 520 if err != nil { 521 return nil, err 522 } 523 524 if multiAttr == unknownVariable { 525 return &ast.Variable{Type: ast.TypeString, Value: ""}, nil 526 } 527 528 for _, element := range multiAttr.Value.([]ast.Variable) { 529 strVal := element.Value.(string) 530 if strVal == config.UnknownVariableValue { 531 return &unknownVariable, nil 532 } 533 534 values = append(values, strVal) 535 } 536 } 537 538 if len(values) == 0 { 539 // If the operation is refresh, it isn't an error for a value to 540 // be unknown. Instead, we return that the value is computed so 541 // that the graph can continue to refresh other nodes. It doesn't 542 // matter because the config isn't interpolated anyways. 543 // 544 // For a Destroy, we're also fine with computed values, since our goal is 545 // only to get destroy nodes for existing resources. 546 // 547 // For an input walk, computed values are okay to return because we're only 548 // looking for missing variables to prompt the user for. 549 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 550 return &unknownVariable, nil 551 } 552 553 return nil, fmt.Errorf( 554 "Resource '%s' does not have attribute '%s' "+ 555 "for variable '%s'", 556 v.ResourceId(), 557 v.Field, 558 v.FullKey()) 559 } 560 561 variable, err := hil.InterfaceToVariable(values) 562 return &variable, err 563 } 564 565 func (i *Interpolater) interpolateComplexTypeAttribute( 566 resourceID string, 567 attributes map[string]string) (ast.Variable, error) { 568 569 attr := attributes[resourceID+".#"] 570 log.Printf("[DEBUG] Interpolating computed complex type attribute %s (%s)", 571 resourceID, attr) 572 573 // In Terraform's internal dotted representation of list-like attributes, the 574 // ".#" count field is marked as unknown to indicate "this whole list is 575 // unknown". We must honor that meaning here so computed references can be 576 // treated properly during the plan phase. 577 if attr == config.UnknownVariableValue { 578 return unknownVariable(), nil 579 } 580 581 // At this stage we don't know whether the item is a list or a map, so we 582 // examine the keys to see whether they are all numeric. 583 var numericKeys []string 584 var allKeys []string 585 numberedListKey := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$") 586 otherListKey := regexp.MustCompile("^" + resourceID + "\\.([^#]+)$") 587 for id, _ := range attributes { 588 if numberedListKey.MatchString(id) { 589 numericKeys = append(numericKeys, id) 590 } 591 if submatches := otherListKey.FindAllStringSubmatch(id, -1); len(submatches) > 0 { 592 allKeys = append(allKeys, submatches[0][1]) 593 } 594 } 595 596 if len(numericKeys) == len(allKeys) { 597 // This is a list 598 var members []string 599 for _, key := range numericKeys { 600 members = append(members, attributes[key]) 601 } 602 sort.Strings(members) 603 return hil.InterfaceToVariable(members) 604 } else { 605 // This is a map 606 members := make(map[string]interface{}) 607 for _, key := range allKeys { 608 members[key] = attributes[resourceID+"."+key] 609 } 610 return hil.InterfaceToVariable(members) 611 } 612 } 613 614 func (i *Interpolater) resourceVariableInfo( 615 scope *InterpolationScope, 616 v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { 617 // Get the module tree that contains our current path. This is 618 // either the current module (path is empty) or a child. 619 modTree := i.Module 620 if len(scope.Path) > 1 { 621 modTree = i.Module.Child(scope.Path[1:]) 622 } 623 624 // Get the resource from the configuration so we can verify 625 // that the resource is in the configuration and so we can access 626 // the configuration if we need to. 627 var cr *config.Resource 628 for _, r := range modTree.Config().Resources { 629 if r.Id() == v.ResourceId() { 630 cr = r 631 break 632 } 633 } 634 if cr == nil { 635 return nil, nil, fmt.Errorf( 636 "Resource '%s' not found for variable '%s'", 637 v.ResourceId(), 638 v.FullKey()) 639 } 640 641 // Get the relevant module 642 module := i.State.ModuleByPath(scope.Path) 643 return module, cr, nil 644 }