github.com/alexissmirnov/terraform@v0.4.3-0.20150423153700-1ef9731a2f14/terraform/interpolate.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "sync" 8 9 "github.com/hashicorp/terraform/config" 10 "github.com/hashicorp/terraform/config/lang/ast" 11 "github.com/hashicorp/terraform/config/module" 12 ) 13 14 const ( 15 // VarEnvPrefix is the prefix of variables that are read from 16 // the environment to set variables here. 17 VarEnvPrefix = "TF_VAR_" 18 ) 19 20 // Interpolater is the structure responsible for determining the values 21 // for interpolations such as `aws_instance.foo.bar`. 22 type Interpolater struct { 23 Operation walkOperation 24 Module *module.Tree 25 State *State 26 StateLock *sync.RWMutex 27 Variables map[string]string 28 } 29 30 // InterpolationScope is the current scope of execution. This is required 31 // since some variables which are interpolated are dependent on what we're 32 // operating on and where we are. 33 type InterpolationScope struct { 34 Path []string 35 Resource *Resource 36 } 37 38 // Values returns the values for all the variables in the given map. 39 func (i *Interpolater) Values( 40 scope *InterpolationScope, 41 vars map[string]config.InterpolatedVariable) (map[string]ast.Variable, error) { 42 result := make(map[string]ast.Variable, len(vars)) 43 44 // Copy the default variables 45 if i.Module != nil && scope != nil { 46 mod := i.Module 47 if len(scope.Path) > 1 { 48 mod = i.Module.Child(scope.Path[1:]) 49 } 50 for _, v := range mod.Config().Variables { 51 for k, val := range v.DefaultsMap() { 52 envKey := VarEnvPrefix + strings.TrimPrefix(k, "var.") 53 if v := os.Getenv(envKey); v != "" { 54 val = v 55 } 56 57 result[k] = ast.Variable{ 58 Value: val, 59 Type: ast.TypeString, 60 } 61 } 62 } 63 } 64 65 for n, rawV := range vars { 66 var err error 67 switch v := rawV.(type) { 68 case *config.CountVariable: 69 err = i.valueCountVar(scope, n, v, result) 70 case *config.ModuleVariable: 71 err = i.valueModuleVar(scope, n, v, result) 72 case *config.PathVariable: 73 err = i.valuePathVar(scope, n, v, result) 74 case *config.ResourceVariable: 75 err = i.valueResourceVar(scope, n, v, result) 76 case *config.SelfVariable: 77 err = i.valueSelfVar(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) valueUserVar( 255 scope *InterpolationScope, 256 n string, 257 v *config.UserVariable, 258 result map[string]ast.Variable) error { 259 val, ok := i.Variables[v.Name] 260 if ok { 261 result[n] = ast.Variable{ 262 Value: val, 263 Type: ast.TypeString, 264 } 265 return nil 266 } 267 268 if _, ok := result[n]; !ok && i.Operation == walkValidate { 269 result[n] = ast.Variable{ 270 Value: config.UnknownVariableValue, 271 Type: ast.TypeString, 272 } 273 return nil 274 } 275 276 // Look up if we have any variables with this prefix because 277 // those are map overrides. Include those. 278 for k, val := range i.Variables { 279 if strings.HasPrefix(k, v.Name+".") { 280 result["var."+k] = ast.Variable{ 281 Value: val, 282 Type: ast.TypeString, 283 } 284 } 285 } 286 287 return nil 288 } 289 290 func (i *Interpolater) computeResourceVariable( 291 scope *InterpolationScope, 292 v *config.ResourceVariable) (string, error) { 293 id := v.ResourceId() 294 if v.Multi { 295 id = fmt.Sprintf("%s.%d", id, v.Index) 296 } 297 298 i.StateLock.RLock() 299 defer i.StateLock.RUnlock() 300 301 // Get the information about this resource variable, and verify 302 // that it exists and such. 303 module, _, err := i.resourceVariableInfo(scope, v) 304 if err != nil { 305 return "", err 306 } 307 308 // If we have no module in the state yet or count, return empty 309 if module == nil || len(module.Resources) == 0 { 310 return "", nil 311 } 312 313 // Get the resource out from the state. We know the state exists 314 // at this point and if there is a state, we expect there to be a 315 // resource with the given name. 316 r, ok := module.Resources[id] 317 if !ok && v.Multi && v.Index == 0 { 318 r, ok = module.Resources[v.ResourceId()] 319 } 320 if !ok { 321 r = nil 322 } 323 if r == nil { 324 return "", fmt.Errorf( 325 "Resource '%s' not found for variable '%s'", 326 id, 327 v.FullKey()) 328 } 329 330 if r.Primary == nil { 331 goto MISSING 332 } 333 334 if attr, ok := r.Primary.Attributes[v.Field]; ok { 335 return attr, nil 336 } 337 338 // At apply time, we can't do the "maybe has it" check below 339 // that we need for plans since parent elements might be computed. 340 // Therefore, it is an error and we're missing the key. 341 // 342 // TODO: test by creating a state and configuration that is referencing 343 // a non-existent variable "foo.bar" where the state only has "foo" 344 // and verify plan works, but apply doesn't. 345 if i.Operation == walkApply { 346 goto MISSING 347 } 348 349 // We didn't find the exact field, so lets separate the dots 350 // and see if anything along the way is a computed set. i.e. if 351 // we have "foo.0.bar" as the field, check to see if "foo" is 352 // a computed list. If so, then the whole thing is computed. 353 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 354 for i := 1; i < len(parts); i++ { 355 // Lists and sets make this 356 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 357 if attr, ok := r.Primary.Attributes[key]; ok { 358 return attr, nil 359 } 360 361 // Maps make this 362 key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) 363 if attr, ok := r.Primary.Attributes[key]; ok { 364 return attr, nil 365 } 366 } 367 } 368 369 MISSING: 370 // If the operation is refresh, it isn't an error for a value to 371 // be unknown. Instead, we return that the value is computed so 372 // that the graph can continue to refresh other nodes. It doesn't 373 // matter because the config isn't interpolated anyways. 374 if i.Operation == walkRefresh { 375 return config.UnknownVariableValue, nil 376 } 377 378 return "", fmt.Errorf( 379 "Resource '%s' does not have attribute '%s' "+ 380 "for variable '%s'", 381 id, 382 v.Field, 383 v.FullKey()) 384 } 385 386 func (i *Interpolater) computeResourceMultiVariable( 387 scope *InterpolationScope, 388 v *config.ResourceVariable) (string, error) { 389 i.StateLock.RLock() 390 defer i.StateLock.RUnlock() 391 392 // Get the information about this resource variable, and verify 393 // that it exists and such. 394 module, cr, err := i.resourceVariableInfo(scope, v) 395 if err != nil { 396 return "", err 397 } 398 399 // Get the count so we know how many to iterate over 400 count, err := cr.Count() 401 if err != nil { 402 return "", fmt.Errorf( 403 "Error reading %s count: %s", 404 v.ResourceId(), 405 err) 406 } 407 408 // If we have no module in the state yet or count, return empty 409 if module == nil || len(module.Resources) == 0 || count == 0 { 410 return "", nil 411 } 412 413 var values []string 414 for i := 0; i < count; i++ { 415 id := fmt.Sprintf("%s.%d", v.ResourceId(), i) 416 417 // If we're dealing with only a single resource, then the 418 // ID doesn't have a trailing index. 419 if count == 1 { 420 id = v.ResourceId() 421 } 422 423 r, ok := module.Resources[id] 424 if !ok { 425 continue 426 } 427 428 if r.Primary == nil { 429 continue 430 } 431 432 attr, ok := r.Primary.Attributes[v.Field] 433 if !ok { 434 continue 435 } 436 437 values = append(values, attr) 438 } 439 440 if len(values) == 0 { 441 return "", fmt.Errorf( 442 "Resource '%s' does not have attribute '%s' "+ 443 "for variable '%s'", 444 v.ResourceId(), 445 v.Field, 446 v.FullKey()) 447 } 448 449 return strings.Join(values, config.InterpSplitDelim), nil 450 } 451 452 func (i *Interpolater) resourceVariableInfo( 453 scope *InterpolationScope, 454 v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { 455 // Get the module tree that contains our current path. This is 456 // either the current module (path is empty) or a child. 457 modTree := i.Module 458 if len(scope.Path) > 1 { 459 modTree = i.Module.Child(scope.Path[1:]) 460 } 461 462 // Get the resource from the configuration so we can verify 463 // that the resource is in the configuration and so we can access 464 // the configuration if we need to. 465 var cr *config.Resource 466 for _, r := range modTree.Config().Resources { 467 if r.Id() == v.ResourceId() { 468 cr = r 469 break 470 } 471 } 472 if cr == nil { 473 return nil, nil, fmt.Errorf( 474 "Resource '%s' not found for variable '%s'", 475 v.ResourceId(), 476 v.FullKey()) 477 } 478 479 // Get the relevant module 480 module := i.State.ModuleByPath(scope.Path) 481 return module, cr, nil 482 }