helper/schema: Schema.SkipCoreTypeCheck flag
When running in v0.12-and-higher mode, this will cause the SDK to report the type of the attribute as "any", effectively skipping type checking on the Core side altogether and checking only in the SDK and provider code. The practical impact of this is to restore the v0.11-style checking behavior of allowing object values to be missing certain attributes as long as they are marked as optional in the schema. The SDK can do this because it uses a unified schema model for both object values and nested blocks, while Terraform Core only supports the idea of "optional" when talking about attributes in nested blocks. This is a continuation of the pile of workarounds that also includes the ConfigMode and AsSingle fields, allowing providers to selectively opt out of new v0.12 behaviors in situations where they conflict with decisions made in the design of the providers in our old world where Terraform Core delegated _all_ validation to providers. This is designed as an opt-in so that we can limit its impact only to specific cases where it's needed and minimize the risk of regressions elsewhere. Providers should use this sparingly only in situations where prevailing usage disagrees with the new expectations of Terraform Core in v0.12. This commit only adds the flag, and does not implement any behavior for it yet. That means this commit can exist in both the v0.11 and v0.12 codebases, allowing for API compatibility. A subsequent commit for v0.12 (not included in v0.11) will then implement this behavior.
This commit is contained in:
parent
1e32ae243c
commit
7f860dc83e
|
@ -95,6 +95,34 @@ type Schema struct {
|
||||||
// behavior, and SchemaConfigModeBlock is not permitted.
|
// behavior, and SchemaConfigModeBlock is not permitted.
|
||||||
ConfigMode SchemaConfigMode
|
ConfigMode SchemaConfigMode
|
||||||
|
|
||||||
|
// SkipCoreTypeCheck, if set, will advertise this attribute to Terraform Core
|
||||||
|
// has being dynamically-typed rather than deriving a type from the schema.
|
||||||
|
// This has the effect of making Terraform Core skip all type-checking of
|
||||||
|
// the value, and thus leaves all type checking up to a combination of this
|
||||||
|
// SDK and the provider's own code.
|
||||||
|
//
|
||||||
|
// This flag does nothing for Terraform versions prior to v0.12, because
|
||||||
|
// in prior versions there was no Core-side typecheck anyway.
|
||||||
|
//
|
||||||
|
// The most practical effect of this flag is to allow object-typed schemas
|
||||||
|
// (specified with Elem: schema.Resource) to pass through Terraform Core
|
||||||
|
// even without all of the object type attributes specified, which may be
|
||||||
|
// useful when using ConfigMode: SchemaConfigModeAttr to achieve
|
||||||
|
// nested-block-like behaviors while using attribute syntax.
|
||||||
|
//
|
||||||
|
// However, by doing so we require type information to be sent and stored
|
||||||
|
// per-object rather than just once statically in the schema, and so this
|
||||||
|
// will change the wire serialization of a resource type in state. Changing
|
||||||
|
// the value of SkipCoreTypeCheck will therefore require a state migration
|
||||||
|
// if there has previously been any release of the provider compatible with
|
||||||
|
// Terraform v0.12.
|
||||||
|
//
|
||||||
|
// SkipCoreTypeCheck can only be set when ConfigMode is SchemaConfigModeAttr,
|
||||||
|
// because nested blocks cannot be decoded by Terraform Core without at
|
||||||
|
// least shallow information about the next level of nested attributes and
|
||||||
|
// blocks.
|
||||||
|
SkipCoreTypeCheck bool
|
||||||
|
|
||||||
// If one of these is set, then this item can come from the configuration.
|
// If one of these is set, then this item can come from the configuration.
|
||||||
// Both cannot be set. If Optional is set, the value is optional. If
|
// Both cannot be set. If Optional is set, the value is optional. If
|
||||||
// Required is set, the value is required.
|
// Required is set, the value is required.
|
||||||
|
@ -719,6 +747,8 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
||||||
|
|
||||||
computedOnly := v.Computed && !v.Optional
|
computedOnly := v.Computed && !v.Optional
|
||||||
|
|
||||||
|
isBlock := false
|
||||||
|
|
||||||
switch v.ConfigMode {
|
switch v.ConfigMode {
|
||||||
case SchemaConfigModeBlock:
|
case SchemaConfigModeBlock:
|
||||||
if _, ok := v.Elem.(*Resource); !ok {
|
if _, ok := v.Elem.(*Resource); !ok {
|
||||||
|
@ -730,19 +760,27 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro
|
||||||
if computedOnly {
|
if computedOnly {
|
||||||
return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k)
|
return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k)
|
||||||
}
|
}
|
||||||
|
isBlock = true
|
||||||
case SchemaConfigModeAttr:
|
case SchemaConfigModeAttr:
|
||||||
// anything goes
|
// anything goes
|
||||||
case SchemaConfigModeAuto:
|
case SchemaConfigModeAuto:
|
||||||
// Since "Auto" for Elem: *Resource would create a nested block,
|
// Since "Auto" for Elem: *Resource would create a nested block,
|
||||||
// and that's impossible inside an attribute, we require it to be
|
// and that's impossible inside an attribute, we require it to be
|
||||||
// explicitly overridden as mode "Attr" for clarity.
|
// explicitly overridden as mode "Attr" for clarity.
|
||||||
if _, ok := v.Elem.(*Resource); ok && attrsOnly {
|
if _, ok := v.Elem.(*Resource); ok {
|
||||||
return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k)
|
isBlock = true
|
||||||
|
if attrsOnly {
|
||||||
|
return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("%s: invalid ConfigMode value", k)
|
return fmt.Errorf("%s: invalid ConfigMode value", k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isBlock && v.SkipCoreTypeCheck {
|
||||||
|
return fmt.Errorf("%s: SkipCoreTypeCheck must be false unless ConfigMode is attribute", k)
|
||||||
|
}
|
||||||
|
|
||||||
if v.Computed && v.Default != nil {
|
if v.Computed && v.Default != nil {
|
||||||
return fmt.Errorf("%s: Default must be nil if computed", k)
|
return fmt.Errorf("%s: Default must be nil if computed", k)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue