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