config: detect and warn about access to "counted" attributes

A common pattern is to conditionally assign to "count"
in a resource in order to decide dynamically whether it
should be created. In that situation it's necessary to
refer to attributes of the resource using the splat
syntax, but historically we didn't show errors in output
expressions and so people "got away with" incorrect usage
in that context.

The intent of this warning is to catch
potentially-problematic usage of attributes on such
resources even if the count happens to be currently
set dynamically to 1, which would not generate the
error. Then the user can quickly locate and fix the
incorrect usage regardless of the current value.
This commit is contained in:
Martin Atkins 2017-11-21 16:22:55 -08:00
parent ba0514106a
commit cc5e480378
1 changed files with 46 additions and 0 deletions

View File

@ -814,6 +814,52 @@ func (c *Config) Validate() tfdiags.Diagnostics {
}
}
// Detect a common mistake of using a "count"ed resource in
// an output value without using the splat or index form.
// Prior to 0.11 this error was silently ignored, but outputs
// now have their errors checked like all other contexts.
//
// TODO: Remove this in 0.12.
for _, v := range o.RawConfig.Variables {
rv, ok := v.(*ResourceVariable)
if !ok {
continue
}
// If the variable seems to be treating the referenced
// resource as a singleton (no count specified) then
// we'll check to make sure it is indeed a singleton.
// It's a warning if not.
if rv.Multi || rv.Index != 0 {
// This reference is treating the resource as a
// multi-resource, so the warning doesn't apply.
continue
}
for _, r := range c.Resources {
if r.Id() != rv.ResourceId() {
continue
}
// We test specifically for the raw string "1" here
// because we _do_ want to generate this warning if
// the user has provided an expression that happens
// to return 1 right now, to catch situations where
// a count might dynamically be set to something
// other than 1 and thus splat syntax is still needed
// to be safe.
if r.RawCount != nil && r.RawCount.Raw != nil && r.RawCount.Raw["count"] != "1" {
diags = diags.Append(tfdiags.SimpleWarning(fmt.Sprintf(
"output %q: must use splat syntax to access %s attribute %q, because it has \"count\" set; use %s.*.%s to obtain a list of the attributes across all instances",
o.Name,
r.Id(), rv.Field,
r.Id(), rv.Field,
)))
}
}
}
}
}