helper/plugin: Implement Schema.SkipCoreTypeCheck
The previous commit added this flag but did not implement it. Here we implement it by adjusting the shape of schema we return to Terraform Core to mark the attribute as untyped and then ensure that gets handled correctly on the SDK side.
This commit is contained in:
parent
7f860dc83e
commit
135121562e
|
@ -27,6 +27,21 @@ func testResourceConfigMode() *schema.Resource {
|
|||
},
|
||||
},
|
||||
},
|
||||
"resource_as_attr_dynamic": {
|
||||
Type: schema.TypeList,
|
||||
ConfigMode: schema.SchemaConfigModeAttr,
|
||||
SkipCoreTypeCheck: true,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +52,14 @@ func testResourceConfigModeCreate(d *schema.ResourceData, meta interface{}) erro
|
|||
}
|
||||
|
||||
func testResourceConfigModeRead(d *schema.ResourceData, meta interface{}) error {
|
||||
const k = "resource_as_attr"
|
||||
if l, ok := d.Get(k).([]interface{}); !ok {
|
||||
return fmt.Errorf("%s should appear as []interface{}, not %T", k, l)
|
||||
} else {
|
||||
for i, item := range l {
|
||||
if _, ok := item.(map[string]interface{}); !ok {
|
||||
return fmt.Errorf("%s[%d] should appear as map[string]interface{}, not %T", k, i, item)
|
||||
for _, k := range []string{"resource_as_attr", "resource_as_attr_dynamic"} {
|
||||
if l, ok := d.Get(k).([]interface{}); !ok {
|
||||
return fmt.Errorf("%s should appear as []interface{}, not %T", k, l)
|
||||
} else {
|
||||
for i, item := range l {
|
||||
if _, ok := item.(map[string]interface{}); !ok {
|
||||
return fmt.Errorf("%s[%d] should appear as map[string]interface{}, not %T", k, i, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,22 @@ resource "test_resource_config_mode" "foo" {
|
|||
foo = "resource_as_attr 1"
|
||||
},
|
||||
]
|
||||
resource_as_attr_dynamic = [
|
||||
{
|
||||
foo = "resource_as_attr_dynamic 0"
|
||||
},
|
||||
{
|
||||
},
|
||||
]
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.1.foo", "resource_as_attr 1"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.#", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.0.foo", "resource_as_attr_dynamic 0"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.1.foo", "default"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
|
@ -39,21 +49,39 @@ resource "test_resource_config_mode" "foo" {
|
|||
foo = "resource_as_attr 0 updated"
|
||||
},
|
||||
]
|
||||
resource_as_attr_dynamic = [
|
||||
{
|
||||
},
|
||||
]
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0 updated"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.0.foo", "default"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
resource_as_attr = []
|
||||
resource_as_attr_dynamic = []
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "0"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.#", "0"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckNoResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#"),
|
||||
resource.TestCheckNoResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.#"),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -52,7 +52,7 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
|
|||
}
|
||||
|
||||
resp.Provider = &proto.Schema{
|
||||
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()),
|
||||
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlockForCore()),
|
||||
}
|
||||
|
||||
for typ, res := range s.provider.ResourcesMap {
|
||||
|
@ -72,26 +72,41 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block {
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlockForCore() *configschema.Block {
|
||||
return schema.InternalMap(s.provider.Schema).CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block {
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlockForCore(name string) *configschema.Block {
|
||||
res := s.provider.ResourcesMap[name]
|
||||
return res.CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlock(name string) *configschema.Block {
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlockForCore(name string) *configschema.Block {
|
||||
dat := s.provider.DataSourcesMap[name]
|
||||
return dat.CoreConfigSchema()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getProviderSchemaBlockForShimming() *configschema.Block {
|
||||
return schema.InternalMap(s.provider.Schema).CoreConfigSchemaForShimming()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getResourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||
res := s.provider.ResourcesMap[name]
|
||||
return res.CoreConfigSchemaForShimming()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) getDatasourceSchemaBlockForShimming(name string) *configschema.Block {
|
||||
dat := s.provider.DataSourcesMap[name]
|
||||
return dat.CoreConfigSchemaForShimming()
|
||||
}
|
||||
|
||||
func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) {
|
||||
resp := &proto.PrepareProviderConfig_Response{}
|
||||
|
||||
block := s.getProviderSchemaBlock()
|
||||
blockForCore := s.getProviderSchemaBlockForCore()
|
||||
blockForShimming := s.getProviderSchemaBlockForShimming()
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -162,19 +177,19 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
configVal, err = block.CoerceValue(configVal)
|
||||
configVal, err = blockForShimming.CoerceValue(configVal)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.Schema)
|
||||
|
||||
warns, errs := s.provider.Validate(config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
preparedConfigMP, err := msgpack.Marshal(configVal, block.ImpliedType())
|
||||
preparedConfigMP, err := msgpack.Marshal(configVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -188,15 +203,16 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
|||
func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *proto.ValidateResourceTypeConfig_Request) (*proto.ValidateResourceTypeConfig_Response, error) {
|
||||
resp := &proto.ValidateResourceTypeConfig_Response{}
|
||||
|
||||
block := s.getResourceSchemaBlock(req.TypeName)
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.ResourcesMap[req.TypeName].Schema)
|
||||
|
||||
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
||||
|
@ -208,15 +224,16 @@ func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *
|
|||
func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *proto.ValidateDataSourceConfig_Request) (*proto.ValidateDataSourceConfig_Response, error) {
|
||||
resp := &proto.ValidateDataSourceConfig_Response{}
|
||||
|
||||
block := s.getDatasourceSchemaBlock(req.TypeName)
|
||||
blockForCore := s.getDatasourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getDatasourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.DataSourcesMap[req.TypeName].Schema)
|
||||
|
||||
warns, errs := s.provider.ValidateDataSource(req.TypeName, config)
|
||||
|
@ -229,7 +246,8 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
resp := &proto.UpgradeResourceState_Response{}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
block := res.CoreConfigSchema()
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
version := int(req.Version)
|
||||
|
||||
|
@ -272,14 +290,14 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
|
||||
// now we need to turn the state into the default json representation, so
|
||||
// that it can be re-decoded using the actual schema.
|
||||
val, err := schema.JSONMapToStateValue(jsonMap, block)
|
||||
val, err := schema.JSONMapToStateValue(jsonMap, blockForShimming)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// encode the final state to the expected msgpack format
|
||||
newStateMP, err := msgpack.Marshal(val, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(val, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -302,7 +320,7 @@ func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]strin
|
|||
// first determine if we need to call the legacy MigrateState func
|
||||
requiresMigrate := version < res.SchemaVersion
|
||||
|
||||
schemaType := res.CoreConfigSchema().ImpliedType()
|
||||
schemaType := res.CoreConfigSchemaForShimming().ImpliedType()
|
||||
|
||||
// if there are any StateUpgraders, then we need to only compare
|
||||
// against the first version there
|
||||
|
@ -395,9 +413,10 @@ func (s *GRPCProviderServer) Stop(_ context.Context, _ *proto.Stop_Request) (*pr
|
|||
func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_Request) (*proto.Configure_Response, error) {
|
||||
resp := &proto.Configure_Response{}
|
||||
|
||||
block := s.getProviderSchemaBlock()
|
||||
blockForCore := s.getProviderSchemaBlockForCore()
|
||||
blockForShimming := s.getProviderSchemaBlockForShimming()
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -405,7 +424,7 @@ func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_R
|
|||
|
||||
s.provider.TerraformVersion = req.TerraformVersion
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.Schema)
|
||||
err = s.provider.Configure(config)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
|
@ -417,9 +436,10 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
resp := &proto.ReadResource_Response{}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
block := res.CoreConfigSchema()
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, block.ImpliedType())
|
||||
stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -442,7 +462,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
// The old provider API used an empty id to signal that the remote
|
||||
// object appears to have been deleted, but our new protocol expects
|
||||
// to see a null value (in the cty sense) in that case.
|
||||
newStateMP, err := msgpack.Marshal(cty.NullVal(block.ImpliedType()), block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(cty.NullVal(blockForCore.ImpliedType()), blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
}
|
||||
|
@ -456,7 +476,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
newInstanceState.Attributes["id"] = newInstanceState.ID
|
||||
|
||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.ResourcesMap[req.TypeName])
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, block.ImpliedType())
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -465,7 +485,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
newStateVal = normalizeNullValues(newStateVal, stateVal, true)
|
||||
newStateVal = copyTimeoutValues(newStateVal, stateVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -490,9 +510,10 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
resp.LegacyTypeSystem = true
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
block := res.CoreConfigSchema()
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, block.ImpliedType())
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -500,7 +521,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
|
||||
create := priorStateVal.IsNull()
|
||||
|
||||
proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, block.ImpliedType())
|
||||
proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -533,7 +554,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
priorState.Meta = priorPrivate
|
||||
|
||||
// turn the proposed state into a legacy configuration
|
||||
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, block)
|
||||
cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(cfg, s.provider.ResourcesMap[req.TypeName].Schema)
|
||||
|
||||
diff, err := s.provider.SimpleDiff(info, priorState, cfg)
|
||||
|
@ -590,13 +611,13 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
// also fixes up the requiresNew keys to match.
|
||||
schema.FixupAsSingleInstanceDiffOut(diff, s.provider.ResourcesMap[req.TypeName])
|
||||
|
||||
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, block.ImpliedType())
|
||||
plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal, err = block.CoerceValue(plannedStateVal)
|
||||
plannedStateVal, err = blockForShimming.CoerceValue(plannedStateVal)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -628,10 +649,10 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
// if this was creating the resource, we need to set any remaining computed
|
||||
// fields
|
||||
if create {
|
||||
plannedStateVal = SetUnknowns(plannedStateVal, block)
|
||||
plannedStateVal = SetUnknowns(plannedStateVal, blockForShimming)
|
||||
}
|
||||
|
||||
plannedMP, err := msgpack.Marshal(plannedStateVal, block.ImpliedType())
|
||||
plannedMP, err := msgpack.Marshal(plannedStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -684,7 +705,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
requiresNew = append(requiresNew, "id")
|
||||
}
|
||||
|
||||
requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, block.ImpliedType())
|
||||
requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -705,15 +726,16 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
}
|
||||
|
||||
res := s.provider.ResourcesMap[req.TypeName]
|
||||
block := res.CoreConfigSchema()
|
||||
blockForCore := s.getResourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, block.ImpliedType())
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, block.ImpliedType())
|
||||
plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -823,13 +845,13 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
}
|
||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.ResourcesMap[req.TypeName])
|
||||
newStateVal := cty.NullVal(block.ImpliedType())
|
||||
newStateVal := cty.NullVal(blockForShimming.ImpliedType())
|
||||
|
||||
// Always return a null value for destroy.
|
||||
// While this is usually indicated by a nil state, check for missing ID or
|
||||
// attributes in the case of a provider failure.
|
||||
if destroy || newInstanceState == nil || newInstanceState.Attributes == nil || newInstanceState.ID == "" {
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -845,7 +867,7 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
|
||||
// We keep the null val if we destroyed the resource, otherwise build the
|
||||
// entire object, even if the new state was nil.
|
||||
newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, block.ImpliedType())
|
||||
newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -855,7 +877,7 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
|
||||
newStateVal = copyTimeoutValues(newStateVal, plannedStateVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -906,14 +928,15 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
resourceType = req.TypeName
|
||||
}
|
||||
|
||||
block := s.getResourceSchemaBlock(resourceType)
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, block.ImpliedType())
|
||||
blockForCore := s.getResourceSchemaBlockForCore(resourceType)
|
||||
blockForShimming := s.getResourceSchemaBlockForShimming(resourceType)
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -942,10 +965,10 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDataSource_Request) (*proto.ReadDataSource_Response, error) {
|
||||
resp := &proto.ReadDataSource_Response{}
|
||||
|
||||
res := s.provider.DataSourcesMap[req.TypeName]
|
||||
block := res.CoreConfigSchema()
|
||||
blockForCore := s.getDatasourceSchemaBlockForCore(req.TypeName)
|
||||
blockForShimming := s.getDatasourceSchemaBlockForShimming(req.TypeName)
|
||||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -955,7 +978,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
Type: req.TypeName,
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
config := terraform.NewResourceConfigShimmed(configVal, blockForShimming)
|
||||
schema.FixupAsSingleResourceConfigIn(config, s.provider.DataSourcesMap[req.TypeName].Schema)
|
||||
|
||||
// we need to still build the diff separately with the Read method to match
|
||||
|
@ -974,7 +997,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
}
|
||||
schema.FixupAsSingleInstanceStateOut(newInstanceState, s.provider.DataSourcesMap[req.TypeName])
|
||||
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, block.ImpliedType())
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, blockForShimming.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
@ -982,7 +1005,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
|
||||
newStateVal = copyTimeoutValues(newStateVal, configVal)
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, blockForCore.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
|
|
|
@ -22,7 +22,18 @@ import (
|
|||
// This method presumes a schema that passes InternalValidate, and so may
|
||||
// panic or produce an invalid result if given an invalid schemaMap.
|
||||
func (m schemaMap) CoreConfigSchema() *configschema.Block {
|
||||
return m.coreConfigSchema(true)
|
||||
return m.coreConfigSchema(true, true)
|
||||
}
|
||||
|
||||
// CoreConfigSchemaForShimming is a variant of CoreConfigSchema that returns
|
||||
// the schema that should be used when applying our shimming behaviors.
|
||||
//
|
||||
// In particular, it ignores the SkipCoreTypeCheck flag on any legacy schemas,
|
||||
// since the shims live on the SDK side and so they need to see the full
|
||||
// type information that we'd normally hide from Terraform Core when skipping
|
||||
// type checking over there.
|
||||
func (m schemaMap) CoreConfigSchemaForShimming() *configschema.Block {
|
||||
return m.coreConfigSchema(true, false)
|
||||
}
|
||||
|
||||
// CoreConfigSchemaWhenShimmed is a variant of CoreConfigSchema that returns
|
||||
|
@ -36,10 +47,10 @@ func (m schemaMap) CoreConfigSchema() *configschema.Block {
|
|||
// This should be used with care only in unusual situations where we need to
|
||||
// work with an already-shimmed value using a new-style schema.
|
||||
func (m schemaMap) CoreConfigSchemaWhenShimmed() *configschema.Block {
|
||||
return m.coreConfigSchema(false)
|
||||
return m.coreConfigSchema(false, false)
|
||||
}
|
||||
|
||||
func (m schemaMap) coreConfigSchema(enableAsSingle bool) *configschema.Block {
|
||||
func (m schemaMap) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.Block {
|
||||
if len(m) == 0 {
|
||||
// We return an actual (empty) object here, rather than a nil,
|
||||
// because a nil result would mean that we don't have a schema at
|
||||
|
@ -54,7 +65,7 @@ func (m schemaMap) coreConfigSchema(enableAsSingle bool) *configschema.Block {
|
|||
|
||||
for name, schema := range m {
|
||||
if schema.Elem == nil {
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(enableAsSingle)
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
||||
continue
|
||||
}
|
||||
if schema.Type == TypeMap {
|
||||
|
@ -68,27 +79,27 @@ func (m schemaMap) coreConfigSchema(enableAsSingle bool) *configschema.Block {
|
|||
sch.Elem = &Schema{
|
||||
Type: TypeString,
|
||||
}
|
||||
ret.Attributes[name] = sch.coreConfigSchemaAttribute(enableAsSingle)
|
||||
ret.Attributes[name] = sch.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch schema.ConfigMode {
|
||||
case SchemaConfigModeAttr:
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(enableAsSingle)
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
||||
case SchemaConfigModeBlock:
|
||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(enableAsSingle)
|
||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(asSingle, skipCoreCheck)
|
||||
default: // SchemaConfigModeAuto, or any other invalid value
|
||||
if schema.Computed && !schema.Optional {
|
||||
// Computed-only schemas are always handled as attributes,
|
||||
// because they never appear in configuration.
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(enableAsSingle)
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
||||
continue
|
||||
}
|
||||
switch schema.Elem.(type) {
|
||||
case *Schema, ValueType:
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(enableAsSingle)
|
||||
ret.Attributes[name] = schema.coreConfigSchemaAttribute(asSingle, skipCoreCheck)
|
||||
case *Resource:
|
||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(enableAsSingle)
|
||||
ret.BlockTypes[name] = schema.coreConfigSchemaBlock(asSingle, skipCoreCheck)
|
||||
default:
|
||||
// Should never happen for a valid schema
|
||||
panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
|
||||
|
@ -103,7 +114,7 @@ func (m schemaMap) coreConfigSchema(enableAsSingle bool) *configschema.Block {
|
|||
// of a schema. This is appropriate only for primitives or collections whose
|
||||
// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
|
||||
// whose elem is a whole resource.
|
||||
func (s *Schema) coreConfigSchemaAttribute(enableAsSingle bool) *configschema.Attribute {
|
||||
func (s *Schema) coreConfigSchemaAttribute(asSingle, skipCoreCheck bool) *configschema.Attribute {
|
||||
// The Schema.DefaultFunc capability adds some extra weirdness here since
|
||||
// it can be combined with "Required: true" to create a sitution where
|
||||
// required-ness is conditional. Terraform Core doesn't share this concept,
|
||||
|
@ -134,7 +145,7 @@ func (s *Schema) coreConfigSchemaAttribute(enableAsSingle bool) *configschema.At
|
|||
}
|
||||
|
||||
return &configschema.Attribute{
|
||||
Type: s.coreConfigSchemaType(enableAsSingle),
|
||||
Type: s.coreConfigSchemaType(asSingle, skipCoreCheck),
|
||||
Optional: opt,
|
||||
Required: reqd,
|
||||
Computed: s.Computed,
|
||||
|
@ -146,9 +157,9 @@ func (s *Schema) coreConfigSchemaAttribute(enableAsSingle bool) *configschema.At
|
|||
// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
|
||||
// a schema. This is appropriate only for collections whose Elem is an instance
|
||||
// of Resource, and will panic otherwise.
|
||||
func (s *Schema) coreConfigSchemaBlock(enableAsSingle bool) *configschema.NestedBlock {
|
||||
func (s *Schema) coreConfigSchemaBlock(asSingle, skipCoreCheck bool) *configschema.NestedBlock {
|
||||
ret := &configschema.NestedBlock{}
|
||||
if nested := schemaMap(s.Elem.(*Resource).Schema).coreConfigSchema(enableAsSingle); nested != nil {
|
||||
if nested := schemaMap(s.Elem.(*Resource).Schema).coreConfigSchema(asSingle, skipCoreCheck); nested != nil {
|
||||
ret.Block = *nested
|
||||
}
|
||||
switch s.Type {
|
||||
|
@ -166,7 +177,7 @@ func (s *Schema) coreConfigSchemaBlock(enableAsSingle bool) *configschema.Nested
|
|||
ret.MinItems = s.MinItems
|
||||
ret.MaxItems = s.MaxItems
|
||||
|
||||
if s.AsSingle && enableAsSingle {
|
||||
if s.AsSingle && asSingle {
|
||||
// In AsSingle mode, we artifically force a TypeList or TypeSet
|
||||
// attribute in the SDK to be treated as a single block by Terraform Core.
|
||||
// This must then be fixed up in the shim code (in helper/plugin) so
|
||||
|
@ -199,7 +210,15 @@ func (s *Schema) coreConfigSchemaBlock(enableAsSingle bool) *configschema.Nested
|
|||
|
||||
// coreConfigSchemaType determines the core config schema type that corresponds
|
||||
// to a particular schema's type.
|
||||
func (s *Schema) coreConfigSchemaType(enableAsSingle bool) cty.Type {
|
||||
func (s *Schema) coreConfigSchemaType(asSingle, skipCoreCheck bool) cty.Type {
|
||||
if skipCoreCheck && s.SkipCoreTypeCheck {
|
||||
// If we're preparing a schema for Terraform Core and the schema is
|
||||
// asking us to skip the Core type-check then we'll tell core that this
|
||||
// attribute is dynamically-typed, so it'll just pass through anything
|
||||
// and let us validate it on the plugin side.
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
switch s.Type {
|
||||
case TypeString:
|
||||
return cty.String
|
||||
|
@ -214,17 +233,17 @@ func (s *Schema) coreConfigSchemaType(enableAsSingle bool) cty.Type {
|
|||
var elemType cty.Type
|
||||
switch set := s.Elem.(type) {
|
||||
case *Schema:
|
||||
elemType = set.coreConfigSchemaType(enableAsSingle)
|
||||
elemType = set.coreConfigSchemaType(asSingle, skipCoreCheck)
|
||||
case ValueType:
|
||||
// This represents a mistake in the provider code, but it's a
|
||||
// common one so we'll just shim it.
|
||||
elemType = (&Schema{Type: set}).coreConfigSchemaType(enableAsSingle)
|
||||
elemType = (&Schema{Type: set}).coreConfigSchemaType(asSingle, skipCoreCheck)
|
||||
case *Resource:
|
||||
// By default we construct a NestedBlock in this case, but this
|
||||
// behavior is selected either for computed-only schemas or
|
||||
// when ConfigMode is explicitly SchemaConfigModeBlock.
|
||||
// See schemaMap.CoreConfigSchema for the exact rules.
|
||||
elemType = schemaMap(set.Schema).coreConfigSchema(enableAsSingle).ImpliedType()
|
||||
elemType = schemaMap(set.Schema).coreConfigSchema(asSingle, skipCoreCheck).ImpliedType()
|
||||
default:
|
||||
if set != nil {
|
||||
// Should never happen for a valid schema
|
||||
|
@ -234,7 +253,7 @@ func (s *Schema) coreConfigSchemaType(enableAsSingle bool) cty.Type {
|
|||
// to be compatible with them.
|
||||
elemType = cty.String
|
||||
}
|
||||
if s.AsSingle && enableAsSingle {
|
||||
if s.AsSingle && asSingle {
|
||||
// In AsSingle mode, we artifically force a TypeList or TypeSet
|
||||
// attribute in the SDK to be treated as a single value by Terraform Core.
|
||||
// This must then be fixed up in the shim code (in helper/plugin) so
|
||||
|
@ -262,7 +281,16 @@ func (s *Schema) coreConfigSchemaType(enableAsSingle bool) cty.Type {
|
|||
// the resource's schema. CoreConfigSchema adds the implicitly required "id"
|
||||
// attribute for top level resources if it doesn't exist.
|
||||
func (r *Resource) CoreConfigSchema() *configschema.Block {
|
||||
return r.coreConfigSchema(true)
|
||||
return r.coreConfigSchema(true, true)
|
||||
}
|
||||
|
||||
// CoreConfigSchemaForShimming is a variant of CoreConfigSchema that returns
|
||||
// the schema that should be used to apply shims on the SDK side.
|
||||
//
|
||||
// In particular, it ignores the SkipCoreTypeCheck flag on any legacy schemas
|
||||
// and uses the real type information instead.
|
||||
func (r *Resource) CoreConfigSchemaForShimming() *configschema.Block {
|
||||
return r.coreConfigSchema(true, false)
|
||||
}
|
||||
|
||||
// CoreConfigSchemaWhenShimmed is a variant of CoreConfigSchema that returns
|
||||
|
@ -276,11 +304,11 @@ func (r *Resource) CoreConfigSchema() *configschema.Block {
|
|||
// This should be used with care only in unusual situations where we need to
|
||||
// work with an already-shimmed value using a new-style schema.
|
||||
func (r *Resource) CoreConfigSchemaWhenShimmed() *configschema.Block {
|
||||
return r.coreConfigSchema(false)
|
||||
return r.coreConfigSchema(false, false)
|
||||
}
|
||||
|
||||
func (r *Resource) coreConfigSchema(enableAsSingle bool) *configschema.Block {
|
||||
block := schemaMap(r.Schema).coreConfigSchema(enableAsSingle)
|
||||
func (r *Resource) coreConfigSchema(asSingle, skipCoreCheck bool) *configschema.Block {
|
||||
block := schemaMap(r.Schema).coreConfigSchema(asSingle, skipCoreCheck)
|
||||
|
||||
if block.Attributes == nil {
|
||||
block.Attributes = map[string]*configschema.Attribute{}
|
||||
|
|
|
@ -445,6 +445,28 @@ func TestSchemaMapCoreConfigSchema(t *testing.T) {
|
|||
BlockTypes: map[string]*configschema.NestedBlock{},
|
||||
}),
|
||||
},
|
||||
"skip core type check": {
|
||||
map[string]*Schema{
|
||||
"list": {
|
||||
Type: TypeList,
|
||||
ConfigMode: SchemaConfigModeAttr,
|
||||
SkipCoreTypeCheck: true,
|
||||
Optional: true,
|
||||
Elem: &Resource{
|
||||
Schema: map[string]*Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
testResource(&configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"list": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Optional: true, // Just so we can progress to provider-driven validation and return the error there
|
||||
},
|
||||
},
|
||||
BlockTypes: map[string]*configschema.NestedBlock{},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
|
|
Loading…
Reference in New Issue