plugin: Use correct schema when marshaling imported resource objects
Previously we were using the type name requested in the import to select the schema, but a provider is free to return additional objects of other types as part of an import result, and so it's important that we perform schema selection separately for each returned object. If we don't do this, we get confusing downstream errors where the resulting object decodes to the wrong type and breaks various invariants expected by Terraform Core. The testResourceImportOther test in the test provider didn't catch this previously because it happened to have an identical schema to the other resource type being imported. Now the schema is changed and also there's a computed attribute we can set as part of the refresh phase to make sure we're completing the Read call properly during import. Refresh was working correctly, but we didn't have any tests for it as part of the import flow.
This commit is contained in:
parent
d1c660eecf
commit
bdcac8792d
|
@ -1,6 +1,8 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,23 +28,9 @@ func testResourceImportOther() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: true,
|
Default: true,
|
||||||
},
|
},
|
||||||
"nested": {
|
"computed": {
|
||||||
Type: schema.TypeSet,
|
|
||||||
Optional: true,
|
|
||||||
ForceNew: true,
|
|
||||||
Elem: &schema.Resource{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"string": {
|
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Computed: true,
|
||||||
Default: "default nested",
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -74,10 +62,13 @@ func testResourceImportOtherUpdate(d *schema.ResourceData, meta interface{}) err
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResourceImportOtherRead(d *schema.ResourceData, meta interface{}) error {
|
func testResourceImportOtherRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
err := d.Set("computed", "hello!")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set 'computed' attribute: %s", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResourceImportOtherDelete(d *schema.ResourceData, meta interface{}) error {
|
func testResourceImportOtherDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.SetId("")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ resource "test_resource_import_other" "foo" {
|
||||||
return fmt.Errorf("no instance with id import_other_main in state")
|
return fmt.Errorf("no instance with id import_other_main in state")
|
||||||
} else if got, want := is.Ephemeral.Type, "test_resource_import_other"; got != want {
|
} else if got, want := is.Ephemeral.Type, "test_resource_import_other"; got != want {
|
||||||
return fmt.Errorf("import_other_main has wrong type %q; want %q", got, want)
|
return fmt.Errorf("import_other_main has wrong type %q; want %q", got, want)
|
||||||
|
} else if got, want := is.Attributes["computed"], "hello!"; got != want {
|
||||||
|
return fmt.Errorf("import_other_main has wrong value %q for its computed attribute; want %q", got, want)
|
||||||
}
|
}
|
||||||
if is, ok := byID["import_other_other"]; !ok {
|
if is, ok := byID["import_other_other"]; !ok {
|
||||||
return fmt.Errorf("no instance with id import_other_other in state")
|
return fmt.Errorf("no instance with id import_other_other in state")
|
||||||
|
|
|
@ -794,8 +794,6 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.ImportResourceState_Request) (*proto.ImportResourceState_Response, error) {
|
func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.ImportResourceState_Request) (*proto.ImportResourceState_Response, error) {
|
||||||
resp := &proto.ImportResourceState_Response{}
|
resp := &proto.ImportResourceState_Response{}
|
||||||
|
|
||||||
block := s.getResourceSchemaBlock(req.TypeName)
|
|
||||||
|
|
||||||
info := &terraform.InstanceInfo{
|
info := &terraform.InstanceInfo{
|
||||||
Type: req.TypeName,
|
Type: req.TypeName,
|
||||||
}
|
}
|
||||||
|
@ -810,6 +808,12 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
||||||
// copy the ID again just to be sure it wasn't missed
|
// copy the ID again just to be sure it wasn't missed
|
||||||
is.Attributes["id"] = is.ID
|
is.Attributes["id"] = is.ID
|
||||||
|
|
||||||
|
resourceType := is.Ephemeral.Type
|
||||||
|
if resourceType == "" {
|
||||||
|
resourceType = req.TypeName
|
||||||
|
}
|
||||||
|
|
||||||
|
block := s.getResourceSchemaBlock(resourceType)
|
||||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, block.ImpliedType())
|
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
@ -828,11 +832,6 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceType := is.Ephemeral.Type
|
|
||||||
if resourceType == "" {
|
|
||||||
resourceType = req.TypeName
|
|
||||||
}
|
|
||||||
|
|
||||||
importedResource := &proto.ImportResourceState_ImportedResource{
|
importedResource := &proto.ImportResourceState_ImportedResource{
|
||||||
TypeName: resourceType,
|
TypeName: resourceType,
|
||||||
State: &proto.DynamicValue{
|
State: &proto.DynamicValue{
|
||||||
|
|
|
@ -459,8 +459,6 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
||||||
func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
|
func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
|
||||||
log.Printf("[TRACE] GRPCProvider: ImportResourceState")
|
log.Printf("[TRACE] GRPCProvider: ImportResourceState")
|
||||||
|
|
||||||
resSchema := p.getResourceSchema(r.TypeName)
|
|
||||||
|
|
||||||
protoReq := &proto.ImportResourceState_Request{
|
protoReq := &proto.ImportResourceState_Request{
|
||||||
TypeName: r.TypeName,
|
TypeName: r.TypeName,
|
||||||
Id: r.ID,
|
Id: r.ID,
|
||||||
|
@ -479,6 +477,7 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
||||||
Private: imported.Private,
|
Private: imported.Private,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resSchema := p.getResourceSchema(resource.TypeName)
|
||||||
state := cty.NullVal(resSchema.Block.ImpliedType())
|
state := cty.NullVal(resSchema.Block.ImpliedType())
|
||||||
if imported.State != nil {
|
if imported.State != nil {
|
||||||
state, err = msgpack.Unmarshal(imported.State.Msgpack, resSchema.Block.ImpliedType())
|
state, err = msgpack.Unmarshal(imported.State.Msgpack, resSchema.Block.ImpliedType())
|
||||||
|
|
Loading…
Reference in New Issue