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