github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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/terraform/config" 13 "github.com/hashicorp/terraform/config/lang/ast" 14 "github.com/hashicorp/terraform/config/module" 15 ) 16 17 const ( 18 // VarEnvPrefix is the prefix of variables that are read from 19 // the environment to set variables here. 20 VarEnvPrefix = "TF_VAR_" 21 ) 22 23 // Interpolater is the structure responsible for determining the values 24 // for interpolations such as `aws_instance.foo.bar`. 25 type Interpolater struct { 26 Operation walkOperation 27 Module *module.Tree 28 State *State 29 StateLock *sync.RWMutex 30 Variables map[string]string 31 } 32 33 // InterpolationScope is the current scope of execution. This is required 34 // since some variables which are interpolated are dependent on what we're 35 // operating on and where we are. 36 type InterpolationScope struct { 37 Path []string 38 Resource *Resource 39 } 40 41 // Values returns the values for all the variables in the given map. 42 func (i *Interpolater) Values( 43 scope *InterpolationScope, 44 vars map[string]config.InterpolatedVariable) (map[string]ast.Variable, error) { 45 result := make(map[string]ast.Variable, len(vars)) 46 47 // Copy the default variables 48 if i.Module != nil && scope != nil { 49 mod := i.Module 50 if len(scope.Path) > 1 { 51 mod = i.Module.Child(scope.Path[1:]) 52 } 53 for _, v := range mod.Config().Variables { 54 for k, val := range v.DefaultsMap() { 55 result[k] = ast.Variable{ 56 Value: val, 57 Type: ast.TypeString, 58 } 59 } 60 } 61 } 62 63 for n, rawV := range vars { 64 var err error 65 switch v := rawV.(type) { 66 case *config.CountVariable: 67 err = i.valueCountVar(scope, n, v, result) 68 case *config.ModuleVariable: 69 err = i.valueModuleVar(scope, n, v, result) 70 case *config.PathVariable: 71 err = i.valuePathVar(scope, n, v, result) 72 case *config.ResourceVariable: 73 err = i.valueResourceVar(scope, n, v, result) 74 case *config.SelfVariable: 75 err = i.valueSelfVar(scope, n, v, result) 76 case *config.SimpleVariable: 77 err = i.valueSimpleVar(scope, n, v, result) 78 case *config.UserVariable: 79 err = i.valueUserVar(scope, n, v, result) 80 default: 81 err = fmt.Errorf("%s: unknown variable type: %T", n, rawV) 82 } 83 84 if err != nil { 85 return nil, err 86 } 87 } 88 89 return result, nil 90 } 91 92 func (i *Interpolater) valueCountVar( 93 scope *InterpolationScope, 94 n string, 95 v *config.CountVariable, 96 result map[string]ast.Variable) error { 97 switch v.Type { 98 case config.CountValueIndex: 99 if scope.Resource == nil { 100 return fmt.Errorf("%s: count.index is only valid within resources", n) 101 } 102 result[n] = ast.Variable{ 103 Value: scope.Resource.CountIndex, 104 Type: ast.TypeInt, 105 } 106 return nil 107 default: 108 return fmt.Errorf("%s: unknown count type: %#v", n, v.Type) 109 } 110 } 111 112 func (i *Interpolater) valueModuleVar( 113 scope *InterpolationScope, 114 n string, 115 v *config.ModuleVariable, 116 result map[string]ast.Variable) error { 117 // If we're computing all dynamic fields, then module vars count 118 // and we mark it as computed. 119 if i.Operation == walkValidate { 120 result[n] = ast.Variable{ 121 Value: config.UnknownVariableValue, 122 Type: ast.TypeString, 123 } 124 return nil 125 } 126 127 // Build the path to the child module we want 128 path := make([]string, len(scope.Path), len(scope.Path)+1) 129 copy(path, scope.Path) 130 path = append(path, v.Name) 131 132 // Grab the lock so that if other interpolations are running or 133 // state is being modified, we'll be safe. 134 i.StateLock.RLock() 135 defer i.StateLock.RUnlock() 136 137 // Get the module where we're looking for the value 138 var value string 139 mod := i.State.ModuleByPath(path) 140 if mod == nil { 141 // If the module doesn't exist, then we can return an empty string. 142 // This happens usually only in Refresh() when we haven't populated 143 // a state. During validation, we semantically verify that all 144 // modules reference other modules, and graph ordering should 145 // ensure that the module is in the state, so if we reach this 146 // point otherwise it really is a panic. 147 value = config.UnknownVariableValue 148 } else { 149 // Get the value from the outputs 150 var ok bool 151 value, ok = mod.Outputs[v.Field] 152 if !ok { 153 // Same reasons as the comment above. 154 value = config.UnknownVariableValue 155 } 156 } 157 158 result[n] = ast.Variable{ 159 Value: value, 160 Type: ast.TypeString, 161 } 162 return nil 163 } 164 165 func (i *Interpolater) valuePathVar( 166 scope *InterpolationScope, 167 n string, 168 v *config.PathVariable, 169 result map[string]ast.Variable) error { 170 switch v.Type { 171 case config.PathValueCwd: 172 wd, err := os.Getwd() 173 if err != nil { 174 return fmt.Errorf( 175 "Couldn't get cwd for var %s: %s", 176 v.FullKey(), err) 177 } 178 179 result[n] = ast.Variable{ 180 Value: wd, 181 Type: ast.TypeString, 182 } 183 case config.PathValueModule: 184 if t := i.Module.Child(scope.Path[1:]); t != nil { 185 result[n] = ast.Variable{ 186 Value: t.Config().Dir, 187 Type: ast.TypeString, 188 } 189 } 190 case config.PathValueRoot: 191 result[n] = ast.Variable{ 192 Value: i.Module.Config().Dir, 193 Type: ast.TypeString, 194 } 195 default: 196 return fmt.Errorf("%s: unknown path type: %#v", n, v.Type) 197 } 198 199 return nil 200 201 } 202 203 func (i *Interpolater) valueResourceVar( 204 scope *InterpolationScope, 205 n string, 206 v *config.ResourceVariable, 207 result map[string]ast.Variable) error { 208 // If we're computing all dynamic fields, then module vars count 209 // and we mark it as computed. 210 if i.Operation == walkValidate { 211 result[n] = ast.Variable{ 212 Value: config.UnknownVariableValue, 213 Type: ast.TypeString, 214 } 215 return nil 216 } 217 218 var attr string 219 var err error 220 if v.Multi && v.Index == -1 { 221 attr, err = i.computeResourceMultiVariable(scope, v) 222 } else { 223 attr, err = i.computeResourceVariable(scope, v) 224 } 225 if err != nil { 226 return err 227 } 228 229 result[n] = ast.Variable{ 230 Value: attr, 231 Type: ast.TypeString, 232 } 233 return nil 234 } 235 236 func (i *Interpolater) valueSelfVar( 237 scope *InterpolationScope, 238 n string, 239 v *config.SelfVariable, 240 result map[string]ast.Variable) error { 241 rv, err := config.NewResourceVariable(fmt.Sprintf( 242 "%s.%s.%d.%s", 243 scope.Resource.Type, 244 scope.Resource.Name, 245 scope.Resource.CountIndex, 246 v.Field)) 247 if err != nil { 248 return err 249 } 250 251 return i.valueResourceVar(scope, n, rv, result) 252 } 253 254 func (i *Interpolater) valueSimpleVar( 255 scope *InterpolationScope, 256 n string, 257 v *config.SimpleVariable, 258 result map[string]ast.Variable) error { 259 // SimpleVars are never handled by Terraform's interpolator 260 result[n] = ast.Variable{ 261 Value: config.UnknownVariableValue, 262 Type: ast.TypeString, 263 } 264 return nil 265 } 266 267 func (i *Interpolater) valueUserVar( 268 scope *InterpolationScope, 269 n string, 270 v *config.UserVariable, 271 result map[string]ast.Variable) error { 272 val, ok := i.Variables[v.Name] 273 if ok { 274 result[n] = ast.Variable{ 275 Value: val, 276 Type: ast.TypeString, 277 } 278 return nil 279 } 280 281 if _, ok := result[n]; !ok && i.Operation == walkValidate { 282 result[n] = ast.Variable{ 283 Value: config.UnknownVariableValue, 284 Type: ast.TypeString, 285 } 286 return nil 287 } 288 289 // Look up if we have any variables with this prefix because 290 // those are map overrides. Include those. 291 for k, val := range i.Variables { 292 if strings.HasPrefix(k, v.Name+".") { 293 result["var."+k] = ast.Variable{ 294 Value: val, 295 Type: ast.TypeString, 296 } 297 } 298 } 299 300 return nil 301 } 302 303 func (i *Interpolater) computeResourceVariable( 304 scope *InterpolationScope, 305 v *config.ResourceVariable) (string, error) { 306 id := v.ResourceId() 307 if v.Multi { 308 id = fmt.Sprintf("%s.%d", id, v.Index) 309 } 310 311 i.StateLock.RLock() 312 defer i.StateLock.RUnlock() 313 314 // Get the information about this resource variable, and verify 315 // that it exists and such. 316 module, _, err := i.resourceVariableInfo(scope, v) 317 if err != nil { 318 return "", err 319 } 320 321 // If we have no module in the state yet or count, return empty 322 if module == nil || len(module.Resources) == 0 { 323 return "", nil 324 } 325 326 // Get the resource out from the state. We know the state exists 327 // at this point and if there is a state, we expect there to be a 328 // resource with the given name. 329 r, ok := module.Resources[id] 330 if !ok && v.Multi && v.Index == 0 { 331 r, ok = module.Resources[v.ResourceId()] 332 } 333 if !ok { 334 r = nil 335 } 336 if r == nil { 337 goto MISSING 338 } 339 340 if r.Primary == nil { 341 goto MISSING 342 } 343 344 if attr, ok := r.Primary.Attributes[v.Field]; ok { 345 return attr, nil 346 } 347 348 // computed list attribute 349 if _, ok := r.Primary.Attributes[v.Field+".#"]; ok { 350 return i.interpolateListAttribute(v.Field, r.Primary.Attributes) 351 } 352 353 // At apply time, we can't do the "maybe has it" check below 354 // that we need for plans since parent elements might be computed. 355 // Therefore, it is an error and we're missing the key. 356 // 357 // TODO: test by creating a state and configuration that is referencing 358 // a non-existent variable "foo.bar" where the state only has "foo" 359 // and verify plan works, but apply doesn't. 360 if i.Operation == walkApply || i.Operation == walkDestroy { 361 goto MISSING 362 } 363 364 // We didn't find the exact field, so lets separate the dots 365 // and see if anything along the way is a computed set. i.e. if 366 // we have "foo.0.bar" as the field, check to see if "foo" is 367 // a computed list. If so, then the whole thing is computed. 368 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 369 for i := 1; i < len(parts); i++ { 370 // Lists and sets make this 371 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 372 if attr, ok := r.Primary.Attributes[key]; ok { 373 return attr, nil 374 } 375 376 // Maps make this 377 key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) 378 if attr, ok := r.Primary.Attributes[key]; ok { 379 return attr, nil 380 } 381 } 382 } 383 384 MISSING: 385 // Validation for missing interpolations should happen at a higher 386 // semantic level. If we reached this point and don't have variables, 387 // just return the computed value. 388 if scope == nil && scope.Resource == nil { 389 return config.UnknownVariableValue, nil 390 } 391 392 // If the operation is refresh, it isn't an error for a value to 393 // be unknown. Instead, we return that the value is computed so 394 // that the graph can continue to refresh other nodes. It doesn't 395 // matter because the config isn't interpolated anyways. 396 // 397 // For a Destroy, we're also fine with computed values, since our goal is 398 // only to get destroy nodes for existing resources. 399 // 400 // For an input walk, computed values are okay to return because we're only 401 // looking for missing variables to prompt the user for. 402 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 403 return config.UnknownVariableValue, nil 404 } 405 406 return "", fmt.Errorf( 407 "Resource '%s' does not have attribute '%s' "+ 408 "for variable '%s'", 409 id, 410 v.Field, 411 v.FullKey()) 412 } 413 414 func (i *Interpolater) computeResourceMultiVariable( 415 scope *InterpolationScope, 416 v *config.ResourceVariable) (string, error) { 417 i.StateLock.RLock() 418 defer i.StateLock.RUnlock() 419 420 // Get the information about this resource variable, and verify 421 // that it exists and such. 422 module, cr, err := i.resourceVariableInfo(scope, v) 423 if err != nil { 424 return "", err 425 } 426 427 // Get the count so we know how many to iterate over 428 count, err := cr.Count() 429 if err != nil { 430 return "", fmt.Errorf( 431 "Error reading %s count: %s", 432 v.ResourceId(), 433 err) 434 } 435 436 // If we have no module in the state yet or count, return empty 437 if module == nil || len(module.Resources) == 0 || count == 0 { 438 return "", nil 439 } 440 441 var values []string 442 for j := 0; j < count; j++ { 443 id := fmt.Sprintf("%s.%d", v.ResourceId(), j) 444 445 // If we're dealing with only a single resource, then the 446 // ID doesn't have a trailing index. 447 if count == 1 { 448 id = v.ResourceId() 449 } 450 451 r, ok := module.Resources[id] 452 if !ok { 453 continue 454 } 455 456 if r.Primary == nil { 457 continue 458 } 459 460 attr, ok := r.Primary.Attributes[v.Field] 461 if !ok { 462 // computed list attribute 463 _, ok := r.Primary.Attributes[v.Field+".#"] 464 if !ok { 465 continue 466 } 467 attr, err = i.interpolateListAttribute(v.Field, r.Primary.Attributes) 468 if err != nil { 469 return "", err 470 } 471 } 472 473 if config.IsStringList(attr) { 474 for _, s := range config.StringList(attr).Slice() { 475 values = append(values, s) 476 } 477 continue 478 } 479 480 // If any value is unknown, the whole thing is unknown 481 if attr == config.UnknownVariableValue { 482 return config.UnknownVariableValue, nil 483 } 484 485 values = append(values, attr) 486 } 487 488 if len(values) == 0 { 489 // If the operation is refresh, it isn't an error for a value to 490 // be unknown. Instead, we return that the value is computed so 491 // that the graph can continue to refresh other nodes. It doesn't 492 // matter because the config isn't interpolated anyways. 493 // 494 // For a Destroy, we're also fine with computed values, since our goal is 495 // only to get destroy nodes for existing resources. 496 // 497 // For an input walk, computed values are okay to return because we're only 498 // looking for missing variables to prompt the user for. 499 if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput { 500 return config.UnknownVariableValue, nil 501 } 502 503 return "", fmt.Errorf( 504 "Resource '%s' does not have attribute '%s' "+ 505 "for variable '%s'", 506 v.ResourceId(), 507 v.Field, 508 v.FullKey()) 509 } 510 511 return config.NewStringList(values).String(), nil 512 } 513 514 func (i *Interpolater) interpolateListAttribute( 515 resourceID string, 516 attributes map[string]string) (string, error) { 517 518 attr := attributes[resourceID+".#"] 519 log.Printf("[DEBUG] Interpolating computed list attribute %s (%s)", 520 resourceID, attr) 521 522 var members []string 523 numberedListMember := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$") 524 for id, value := range attributes { 525 if numberedListMember.MatchString(id) { 526 members = append(members, value) 527 } 528 } 529 530 sort.Strings(members) 531 return config.NewStringList(members).String(), nil 532 } 533 534 func (i *Interpolater) resourceVariableInfo( 535 scope *InterpolationScope, 536 v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { 537 // Get the module tree that contains our current path. This is 538 // either the current module (path is empty) or a child. 539 modTree := i.Module 540 if len(scope.Path) > 1 { 541 modTree = i.Module.Child(scope.Path[1:]) 542 } 543 544 // Get the resource from the configuration so we can verify 545 // that the resource is in the configuration and so we can access 546 // the configuration if we need to. 547 var cr *config.Resource 548 for _, r := range modTree.Config().Resources { 549 if r.Id() == v.ResourceId() { 550 cr = r 551 break 552 } 553 } 554 if cr == nil { 555 return nil, nil, fmt.Errorf( 556 "Resource '%s' not found for variable '%s'", 557 v.ResourceId(), 558 v.FullKey()) 559 } 560 561 // Get the relevant module 562 module := i.State.ModuleByPath(scope.Path) 563 return module, cr, nil 564 }