func (i *Interpolater) resourceVariableInfo( scope *InterpolationScope, v *config.ResourceVariable) (*ModuleState, *config.Resource, error) { // Get the module tree that contains our current path. This is // either the current module (path is empty) or a child. modTree := i.Module if len(scope.Path) > 1 { modTree = i.Module.Child(scope.Path[1:]) } // Get the resource from the configuration so we can verify // that the resource is in the configuration and so we can access // the configuration if we need to. var cr *config.Resource for _, r := range modTree.Config().Resources { if r.Id() == v.ResourceId() { cr = r break } } if cr == nil { return nil, nil, fmt.Errorf( "Resource '%s' not found for variable '%s'", v.ResourceId(), v.FullKey()) } // Get the relevant module module := i.State.ModuleByPath(scope.Path) return module, cr, nil }
func (i *Interpolater) computeResourceMultiVariable( scope *InterpolationScope, v *config.ResourceVariable) (string, error) { i.StateLock.RLock() defer i.StateLock.RUnlock() // Get the information about this resource variable, and verify // that it exists and such. module, cr, err := i.resourceVariableInfo(scope, v) if err != nil { return "", err } // Get the count so we know how many to iterate over count, err := cr.Count() if err != nil { return "", fmt.Errorf( "Error reading %s count: %s", v.ResourceId(), err) } // If we have no module in the state yet or count, return empty if module == nil || len(module.Resources) == 0 || count == 0 { return "", nil } var values []string for i := 0; i < count; i++ { id := fmt.Sprintf("%s.%d", v.ResourceId(), i) // If we're dealing with only a single resource, then the // ID doesn't have a trailing index. if count == 1 { id = v.ResourceId() } r, ok := module.Resources[id] if !ok { continue } if r.Primary == nil { continue } attr, ok := r.Primary.Attributes[v.Field] if !ok { continue } values = append(values, attr) } if len(values) == 0 { return "", fmt.Errorf( "Resource '%s' does not have attribute '%s' "+ "for variable '%s'", v.ResourceId(), v.Field, v.FullKey()) } return strings.Join(values, config.InterpSplitDelim), nil }
func (i *Interpolater) computeResourceVariable( scope *InterpolationScope, v *config.ResourceVariable) (string, error) { id := v.ResourceId() if v.Multi { id = fmt.Sprintf("%s.%d", id, v.Index) } i.StateLock.RLock() defer i.StateLock.RUnlock() // Get the information about this resource variable, and verify // that it exists and such. module, _, err := i.resourceVariableInfo(scope, v) if err != nil { return "", err } // If we have no module in the state yet or count, return empty if module == nil || len(module.Resources) == 0 { return "", nil } // Get the resource out from the state. We know the state exists // at this point and if there is a state, we expect there to be a // resource with the given name. r, ok := module.Resources[id] if !ok && v.Multi && v.Index == 0 { r, ok = module.Resources[v.ResourceId()] } if !ok { r = nil } if r == nil { goto MISSING } if r.Primary == nil { goto MISSING } if attr, ok := r.Primary.Attributes[v.Field]; ok { return attr, nil } // At apply time, we can't do the "maybe has it" check below // that we need for plans since parent elements might be computed. // Therefore, it is an error and we're missing the key. // // TODO: test by creating a state and configuration that is referencing // a non-existent variable "foo.bar" where the state only has "foo" // and verify plan works, but apply doesn't. if i.Operation == walkApply { goto MISSING } // We didn't find the exact field, so lets separate the dots // and see if anything along the way is a computed set. i.e. if // we have "foo.0.bar" as the field, check to see if "foo" is // a computed list. If so, then the whole thing is computed. if parts := strings.Split(v.Field, "."); len(parts) > 1 { for i := 1; i < len(parts); i++ { // Lists and sets make this key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) if attr, ok := r.Primary.Attributes[key]; ok { return attr, nil } // Maps make this key = fmt.Sprintf("%s", strings.Join(parts[:i], ".")) if attr, ok := r.Primary.Attributes[key]; ok { return attr, nil } } } MISSING: // Validation for missing interpolations should happen at a higher // semantic level. If we reached this point and don't have variables, // just return the computed value. if scope == nil || scope.Resource == nil { return config.UnknownVariableValue, nil } // If the operation is refresh, it isn't an error for a value to // be unknown. Instead, we return that the value is computed so // that the graph can continue to refresh other nodes. It doesn't // matter because the config isn't interpolated anyways. if i.Operation == walkRefresh { return config.UnknownVariableValue, nil } return "", fmt.Errorf( "Resource '%s' does not have attribute '%s' "+ "for variable '%s'", id, v.Field, v.FullKey()) }