This introduces the "refinements" concept from upstream cty, which allows Terraform to track some additional information about unknown values that constrains their possible range even though we don't yet know the final value.
value whose type identifier is zero and whose value is unspecified and
meaningless.
value, described in more detail below.
| `type` Pattern | MessagePack Representation |
|---|---|
@ -91,6 +90,64 @@ in the table below, regardless of type:
| `["tuple",TYPES]` | A MessagePack array with one element per element described by the `TYPES` array. The element values are constructed by applying these same mapping rules to the corresponding element of `TYPES`. |
| `"dynamic"` | A MessagePack array with exactly two elements. The first element is a MessagePack binary value containing a JSON-serialized type constraint in the same format described in this table. The second element is the result of applying these same mapping rules to the value with the type given in the first element. This special type constraint represents values whose types will be decided only at runtime. |
Unknown values have two possible representations, both using
// Unicode combining characters edge-case: we match the prefix
// in terms of unicode code units rather than grapheme clusters,
// which is inconsistent with our string processing elsewhere but
// would be a breaking change to fix that bug now.
cty.StringVal("\U0001f937\u200d\u2642"),// "Man Shrugging" is encoded as "Person Shrugging" followed by zero-width joiner and then the masculine gender presentation modifier
cty.StringVal("\U0001f937"),// Just the "Person Shrugging" character without any modifiers
`.name: final value cty.NullVal(cty.String) does not conform to planning placeholder cty.UnknownVal(cty.String).RefineNotNull()`,
},
},
{
&configschema.Block{
Attributes:map[string]*configschema.Attribute{
"name":{
Type:cty.String,
Required:true,
},
},
},
cty.ObjectVal(map[string]cty.Value{
"name":cty.UnknownVal(cty.String).Refine().
StringPrefix("boop:").
NewValue(),
}),
cty.ObjectVal(map[string]cty.Value{
"name":cty.StringVal("thingy"),
}),
[]string{
`.name: final value cty.StringVal("thingy") does not conform to planning placeholder cty.UnknownVal(cty.String).Refine().StringPrefixFull("boop:").NewValue()`,
`.set: count in plan (cty.UnknownVal(cty.Number)) disagrees with count in config (cty.NumberIntVal(1))`,
`.list: count in plan (cty.UnknownVal(cty.Number)) disagrees with count in config (cty.NumberIntVal(1))`,
`.map: count in plan (cty.UnknownVal(cty.Number)) disagrees with count in config (cty.NumberIntVal(1))`,
`.set: count in plan (cty.UnknownVal(cty.Number).Refine().NotNull().NumberLowerBound(cty.NumberIntVal(0), true).NumberUpperBound(cty.NumberIntVal(9.223372036854775807e+18), true).NewValue()) disagrees with count in config (cty.NumberIntVal(1))`,
`.list: count in plan (cty.UnknownVal(cty.Number).Refine().NotNull().NumberLowerBound(cty.NumberIntVal(0), true).NumberUpperBound(cty.NumberIntVal(9.223372036854775807e+18), true).NewValue()) disagrees with count in config (cty.NumberIntVal(1))`,
`.map: count in plan (cty.UnknownVal(cty.Number).Refine().NotNull().NumberLowerBound(cty.NumberIntVal(0), true).NumberUpperBound(cty.NumberIntVal(9.223372036854775807e+18), true).NewValue()) disagrees with count in config (cty.NumberIntVal(1))`,
},
},
"refined unknown values can become less refined":{
// Providers often can't preserve refinements through the provider
// wire protocol: although we do have a defined serialization for
// it, most providers were written before there was any such
// thing as refinements, and in future there might be new
// refinements that even refinement-aware providers don't know
// how to preserve, so we allow them to get dropped here as