func (i *Interpolater) resourceCountKeys( ms *ModuleState, cr *config.Resource, v *config.ResourceVariable) ([]string, error) { id := v.ResourceId() // If we're NOT applying, then we assume we can read the count // from the state. Plan and so on may not have any state yet so // we do a full interpolation. if i.Operation != walkApply { count, err := cr.Count() if err != nil { return nil, err } result := make([]string, count) for i := 0; i < count; i++ { result[i] = fmt.Sprintf("%s.%d", id, i) } return result, nil } // We need to determine the list of resource keys to get values from. // This needs to be sorted so the order is deterministic. We used to // use "cr.Count()" but that doesn't work if the count is interpolated // and we can't guarantee that so we instead depend on the state. var resourceKeys []string for k, _ := range ms.Resources { // If we don't have the right prefix then ignore it if k != id && !strings.HasPrefix(k, id+".") { continue } // Add it to the list resourceKeys = append(resourceKeys, k) } sort.Strings(resourceKeys) return resourceKeys, nil }
func (i *Interpolater) resourceCountMax( ms *ModuleState, cr *config.Resource, v *config.ResourceVariable) (int, error) { id := v.ResourceId() // If we're NOT applying, then we assume we can read the count // from the state. Plan and so on may not have any state yet so // we do a full interpolation. if i.Operation != walkApply { count, err := cr.Count() if err != nil { return 0, err } return count, nil } // We need to determine the list of resource keys to get values from. // This needs to be sorted so the order is deterministic. We used to // use "cr.Count()" but that doesn't work if the count is interpolated // and we can't guarantee that so we instead depend on the state. max := -1 for k, _ := range ms.Resources { // Get the index number for this resource index := "" if k == id { // If the key is the id, then its just 0 (no explicit index) index = "0" } else if strings.HasPrefix(k, id+".") { // Grab the index number out of the state index = k[len(id+"."):] if idx := strings.IndexRune(index, '.'); idx >= 0 { index = index[:idx] } } // If there was no index then this resource didn't match // the one we're looking for, exit. if index == "" { continue } // Turn the index into an int raw, err := strconv.ParseInt(index, 0, 0) if err != nil { return 0, fmt.Errorf( "%s: error parsing index %q as int: %s", id, index, err) } // Keep track of this index if its the max if new := int(raw); new > max { max = new } } // If we never found any matching resources in the state, we // have zero. if max == -1 { return 0, nil } // The result value is "max+1" because we're returning the // max COUNT, not the max INDEX, and we zero-index. return max + 1, nil }
func (c *walkContext) computeResourceMultiVariable( v *config.ResourceVariable) (string, error) { c.Context.sl.RLock() defer c.Context.sl.RUnlock() // Get the resource from the configuration so we can know how // many of the resource there is. var cr *config.Resource for _, r := range c.Context.module.Config().Resources { if r.Id() == v.ResourceId() { cr = r break } } if cr == nil { return "", fmt.Errorf( "Resource '%s' not found for variable '%s'", v.ResourceId(), v.FullKey()) } // Get the relevant module // TODO: Not use only root module module := c.Context.state.RootModule() count, err := cr.Count() if err != nil { return "", fmt.Errorf( "Error reading %s count: %s", v.ResourceId(), err) } // If we have no count, return empty if 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 }