mirror of https://github.com/hashicorp/terraform
Previously we would construct a proposed new state with unknown values in place of any not-set-in-config computed attributes, trying to save the provider a little work in specifying that itself. Unfortunately that turns out to be problematic because it conflates two concerns: attributes can be explicitly set in configuration to an unknown value, in which case the final result of that unknown overrides any default value the provider might normally populate. In other words, this allows the provider to recognize in the proposed new state the difference between an Optional+Computed attribute being set to unknown in the config vs not being set in the config at all. The provider now has the responsibility to replace these proposed null values with unknown values during PlanResourceChange if it expects to select a value during the apply step. It may also populate a known value if the final result can be predicted at plan time, as is the case for constant defaults specified in the provider code. This change comes from a realization that from core's perspective the helper/schema ideas of zero values, explicit default values, and customizediff tweaks are all just examples of "defaults", and by allowing the provider to see during plan whether these attributes are being explicitly set in configuration and thus decide whether the default will be provided immediately during plan or deferred until apply.pull/20265/head
parent
651ee113ac
commit
c794bf5bcc
@ -0,0 +1,68 @@
|
||||
package objchange
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// AllAttributesNull constructs a non-null cty.Value of the object type implied
|
||||
// by the given schema that has all of its leaf attributes set to null and all
|
||||
// of its nested block collections set to zero-length.
|
||||
//
|
||||
// This simulates what would result from decoding an empty configuration block
|
||||
// with the given schema, except that it does not produce errors
|
||||
func AllAttributesNull(schema *configschema.Block) cty.Value {
|
||||
vals := make(map[string]cty.Value)
|
||||
ty := schema.ImpliedType()
|
||||
|
||||
for name := range schema.Attributes {
|
||||
aty := ty.AttributeType(name)
|
||||
vals[name] = cty.NullVal(aty)
|
||||
}
|
||||
|
||||
for name, blockS := range schema.BlockTypes {
|
||||
aty := ty.AttributeType(name)
|
||||
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingSingle:
|
||||
// NestingSingle behaves like an object attribute, which decodes
|
||||
// as null when it's not present in configuration.
|
||||
vals[name] = cty.NullVal(aty)
|
||||
default:
|
||||
// All other nesting types decode as "empty" when not present, but
|
||||
// empty values take different forms depending on the type.
|
||||
switch {
|
||||
case aty.IsListType():
|
||||
vals[name] = cty.ListValEmpty(aty.ElementType())
|
||||
case aty.IsSetType():
|
||||
vals[name] = cty.SetValEmpty(aty.ElementType())
|
||||
case aty.IsMapType():
|
||||
vals[name] = cty.MapValEmpty(aty.ElementType())
|
||||
case aty.Equals(cty.DynamicPseudoType):
|
||||
// We use DynamicPseudoType in situations where there's a
|
||||
// nested attribute of DynamicPseudoType, since the schema
|
||||
// system cannot predict the final type until it knows exactly
|
||||
// how many elements there will be. However, since we're
|
||||
// trying to behave as if there are _no_ elements, we know
|
||||
// we're producing either an empty tuple or empty object
|
||||
// and just need to distinguish these two cases.
|
||||
switch blockS.Nesting {
|
||||
case configschema.NestingList:
|
||||
vals[name] = cty.EmptyTupleVal
|
||||
case configschema.NestingMap:
|
||||
vals[name] = cty.EmptyObjectVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By the time we get down here we should always have set a value.
|
||||
// If not, that suggests a missing case in the above switches.
|
||||
if _, ok := vals[name]; !ok {
|
||||
panic(fmt.Sprintf("failed to create empty value for nested block %q", name))
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(vals)
|
||||
}
|
||||
Loading…
Reference in new issue