github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/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 result[k] = ast.Variable{ 53 Value: val, 54 Type: ast.TypeString, 55 } 56 } 57 } 58 } 59 60 for n, rawV := range vars { 61 var err error 62 switch v := rawV.(type) { 63 case *config.CountVariable: 64 err = i.valueCountVar(scope, n, v, result) 65 case *config.ModuleVariable: 66 err = i.valueModuleVar(scope, n, v, result) 67 case *config.PathVariable: 68 err = i.valuePathVar(scope, n, v, result) 69 case *config.ResourceVariable: 70 err = i.valueResourceVar(scope, n, v, result) 71 case *config.SelfVariable: 72 err = i.valueSelfVar(scope, n, v, result) 73 case *config.UserVariable: 74 err = i.valueUserVar(scope, n, v, result) 75 default: 76 err = fmt.Errorf("%s: unknown variable type: %T", n, rawV) 77 } 78 79 if err != nil { 80 return nil, err 81 } 82 } 83 84 return result, nil 85 } 86 87 func (i *Interpolater) valueCountVar( 88 scope *InterpolationScope, 89 n string, 90 v *config.CountVariable, 91 result map[string]ast.Variable) error { 92 switch v.Type { 93 case config.CountValueIndex: 94 if scope.Resource == nil { 95 return fmt.Errorf("%s: count.index is only valid within resources", n) 96 } 97 result[n] = ast.Variable{ 98 Value: scope.Resource.CountIndex, 99 Type: ast.TypeInt, 100 } 101 return nil 102 default: 103 return fmt.Errorf("%s: unknown count type: %#v", n, v.Type) 104 } 105 } 106 107 func (i *Interpolater) valueModuleVar( 108 scope *InterpolationScope, 109 n string, 110 v *config.ModuleVariable, 111 result map[string]ast.Variable) error { 112 // If we're computing all dynamic fields, then module vars count 113 // and we mark it as computed. 114 if i.Operation == walkValidate { 115 result[n] = ast.Variable{ 116 Value: config.UnknownVariableValue, 117 Type: ast.TypeString, 118 } 119 return nil 120 } 121 122 // Build the path to the child module we want 123 path := make([]string, len(scope.Path), len(scope.Path)+1) 124 copy(path, scope.Path) 125 path = append(path, v.Name) 126 127 // Grab the lock so that if other interpolations are running or 128 // state is being modified, we'll be safe. 129 i.StateLock.RLock() 130 defer i.StateLock.RUnlock() 131 132 // Get the module where we're looking for the value 133 var value string 134 mod := i.State.ModuleByPath(path) 135 if mod == nil { 136 // If the module doesn't exist, then we can return an empty string. 137 // This happens usually only in Refresh() when we haven't populated 138 // a state. During validation, we semantically verify that all 139 // modules reference other modules, and graph ordering should 140 // ensure that the module is in the state, so if we reach this 141 // point otherwise it really is a panic. 142 value = config.UnknownVariableValue 143 } else { 144 // Get the value from the outputs 145 var ok bool 146 value, ok = mod.Outputs[v.Field] 147 if !ok { 148 // Same reasons as the comment above. 149 value = config.UnknownVariableValue 150 } 151 } 152 153 result[n] = ast.Variable{ 154 Value: value, 155 Type: ast.TypeString, 156 } 157 return nil 158 } 159 160 func (i *Interpolater) valuePathVar( 161 scope *InterpolationScope, 162 n string, 163 v *config.PathVariable, 164 result map[string]ast.Variable) error { 165 switch v.Type { 166 case config.PathValueCwd: 167 wd, err := os.Getwd() 168 if err != nil { 169 return fmt.Errorf( 170 "Couldn't get cwd for var %s: %s", 171 v.FullKey(), err) 172 } 173 174 result[n] = ast.Variable{ 175 Value: wd, 176 Type: ast.TypeString, 177 } 178 case config.PathValueModule: 179 if t := i.Module.Child(scope.Path[1:]); t != nil { 180 result[n] = ast.Variable{ 181 Value: t.Config().Dir, 182 Type: ast.TypeString, 183 } 184 } 185 case config.PathValueRoot: 186 result[n] = ast.Variable{ 187 Value: i.Module.Config().Dir, 188 Type: ast.TypeString, 189 } 190 default: 191 return fmt.Errorf("%s: unknown path type: %#v", n, v.Type) 192 } 193 194 return nil 195 196 } 197 198 func (i *Interpolater) valueResourceVar( 199 scope *InterpolationScope, 200 n string, 201 v *config.ResourceVariable, 202 result map[string]ast.Variable) error { 203 // If we're computing all dynamic fields, then module vars count 204 // and we mark it as computed. 205 if i.Operation == walkValidate { 206 result[n] = ast.Variable{ 207 Value: config.UnknownVariableValue, 208 Type: ast.TypeString, 209 } 210 return nil 211 } 212 213 var attr string 214 var err error 215 if v.Multi && v.Index == -1 { 216 attr, err = i.computeResourceMultiVariable(scope, v) 217 } else { 218 attr, err = i.computeResourceVariable(scope, v) 219 } 220 if err != nil { 221 return err 222 } 223 224 result[n] = ast.Variable{ 225 Value: attr, 226 Type: ast.TypeString, 227 } 228 return nil 229 } 230 231 func (i *Interpolater) valueSelfVar( 232 scope *InterpolationScope, 233 n string, 234 v *config.SelfVariable, 235 result map[string]ast.Variable) error { 236 rv, err := config.NewResourceVariable(fmt.Sprintf( 237 "%s.%s.%d.%s", 238 scope.Resource.Type, 239 scope.Resource.Name, 240 scope.Resource.CountIndex, 241 v.Field)) 242 if err != nil { 243 return err 244 } 245 246 return i.valueResourceVar(scope, n, rv, result) 247 } 248 249 func (i *Interpolater) valueUserVar( 250 scope *InterpolationScope, 251 n string, 252 v *config.UserVariable, 253 result map[string]ast.Variable) error { 254 val, ok := i.Variables[v.Name] 255 if ok { 256 result[n] = ast.Variable{ 257 Value: val, 258 Type: ast.TypeString, 259 } 260 return nil 261 } 262 263 if _, ok := result[n]; !ok && i.Operation == walkValidate { 264 result[n] = ast.Variable{ 265 Value: config.UnknownVariableValue, 266 Type: ast.TypeString, 267 } 268 return nil 269 } 270 271 // Look up if we have any variables with this prefix because 272 // those are map overrides. Include those. 273 for k, val := range i.Variables { 274 if strings.HasPrefix(k, v.Name+".") { 275 result["var."+k] = ast.Variable{ 276 Value: val, 277 Type: ast.TypeString, 278 } 279 } 280 } 281 282 return nil 283 } 284 285 func (i *Interpolater) computeResourceVariable( 286 scope *InterpolationScope, 287 v *config.ResourceVariable) (string, error) { 288 id := v.ResourceId() 289 if v.Multi { 290 id = fmt.Sprintf("%s.%d", id, v.Index) 291 } 292 293 i.StateLock.RLock() 294 defer i.StateLock.RUnlock() 295 296 // Get the information about this resource variable, and verify 297 // that it exists and such. 298 module, _, err := i.resourceVariableInfo(scope, v) 299 if err != nil { 300 return "", err 301 } 302 303 // If we have no module in the state yet or count, return empty 304 if module == nil || len(module.Resources) == 0 { 305 return "", nil 306 } 307 308 // Get the resource out from the state. We know the state exists 309 // at this point and if there is a state, we expect there to be a 310 // resource with the given name. 311 r, ok := module.Resources[id] 312 if !ok && v.Multi && v.Index == 0 { 313 r, ok = module.Resources[v.ResourceId()] 314 } 315 if !ok { 316 r = nil 317 } 318 if r == nil { 319 goto MISSING 320 } 321 322 if r.Primary == nil { 323 goto MISSING 324 } 325 326 if attr, ok := r.Primary.Attributes[v.Field]; ok { 327 return attr, nil 328 } 329 330 // At apply time, we can't do the "maybe has it" check below 331 // that we need for plans since parent elements might be computed. 332 // Therefore, it is an error and we're missing the key. 333 // 334 // TODO: test by creating a state and configuration that is referencing 335 // a non-existent variable "foo.bar" where the state only has "foo" 336 // and verify plan works, but apply doesn't. 337 if i.Operation == walkApply { 338 goto MISSING 339 } 340 341 // We didn't find the exact field, so lets separate the dots 342 // and see if anything along the way is a computed set. i.e. if 343 // we have "foo.0.bar" as the field, check to see if "foo" is 344 // a computed list. If so, then the whole thing is computed. 345 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 346 for i := 1; i < len(parts); i++ { 347 // Lists and sets make this 348 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 349 if attr, ok := r.Primary.Attributes[key]; ok { 350 return attr, nil 351 } 352 353 // Maps make this 354 key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) 355 if attr, ok := r.Primary.Attributes[key]; ok { 356 return attr, nil 357 } 358 } 359 } 360 361 MISSING: 362 // Validation for missing interpolations should happen at a higher 363 // semantic level. If we reached this point and don't have variables, 364 // just return the computed value. 365 if scope == nil || scope.Resource == nil { 366 return config.UnknownVariableValue, nil 367 } 368 369 // If the operation is refresh, it isn't an error for a value to 370 // be unknown. Instead, we return that the value is computed so 371 // that the graph can continue to refresh other nodes. It doesn't 372 // matter because the config isn't interpolated anyways. 373 if i.Operation == walkRefresh { 374 return config.UnknownVariableValue, nil 375 } 376 377 return "", fmt.Errorf( 378 "Resource '%s' does not have attribute '%s' "+ 379 "for variable '%s'", 380 id, 381 v.Field, 382 v.FullKey()) 383 } 384 385 func (i *Interpolater) computeResourceMultiVariable( 386 scope *InterpolationScope, 387 v *config.ResourceVariable) (string, error) { 388 i.StateLock.RLock() 389 defer i.StateLock.RUnlock() 390 391 // Get the information about this resource variable, and verify 392 // that it exists and such. 393 module, cr, err := i.resourceVariableInfo(scope, v) 394 if err != nil { 395 return "", err 396 } 397 398 // Get the count so we know how many to iterate over 399 count, err := cr.Count() 400 if err != nil { 401 return "", fmt.Errorf( 402 "Error reading %s count: %s", 403 v.ResourceId(), 404 err) 405 } 406 407 // If we have no module in the state yet or count, return empty 408 if module == nil || len(module.Resources) == 0 || count == 0 { 409 return "", nil 410 } 411 412 var values []string 413 for i := 0; i < count; i++ { 414 id := fmt.Sprintf("%s.%d", v.ResourceId(), i) 415 416 // If we're dealing with only a single resource, then the 417 // ID doesn't have a trailing index. 418 if count == 1 { 419 id = v.ResourceId() 420 } 421 422 r, ok := module.Resources[id] 423 if !ok { 424 continue 425 } 426 427 if r.Primary == nil { 428 continue 429 } 430 431 attr, ok := r.Primary.Attributes[v.Field] 432 if !ok { 433 continue 434 } 435 436 values = append(values, attr) 437 } 438 439 if len(values) == 0 { 440 return "", fmt.Errorf( 441 "Resource '%s' does not have attribute '%s' "+ 442 "for variable '%s'", 443 v.ResourceId(), 444 v.Field, 445 v.FullKey()) 446 } 447 448 return strings.Join(values, config.InterpSplitDelim), nil 449 } 450 451 func (i *Interpolater) resourceVariableInfo( 452 scope *InterpolationScope, 453 v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { 454 // Get the module tree that contains our current path. This is 455 // either the current module (path is empty) or a child. 456 modTree := i.Module 457 if len(scope.Path) > 1 { 458 modTree = i.Module.Child(scope.Path[1:]) 459 } 460 461 // Get the resource from the configuration so we can verify 462 // that the resource is in the configuration and so we can access 463 // the configuration if we need to. 464 var cr *config.Resource 465 for _, r := range modTree.Config().Resources { 466 if r.Id() == v.ResourceId() { 467 cr = r 468 break 469 } 470 } 471 if cr == nil { 472 return nil, nil, fmt.Errorf( 473 "Resource '%s' not found for variable '%s'", 474 v.ResourceId(), 475 v.FullKey()) 476 } 477 478 // Get the relevant module 479 module := i.State.ModuleByPath(scope.Path) 480 return module, cr, nil 481 }