return error for invalid resource import

Most legacy provider resources do not implement any import functionality
other than returning an empty object with the given ID, relying on core
to later read that resource and obtain the complete state. Because of
this, we need to check the response from ReadResource for a null value,
and use that as an indication the import id was invalid.
This commit is contained in:
James Bardin 2021-05-25 17:06:25 -04:00
parent 332045a4e4
commit 99d0266585
2 changed files with 72 additions and 0 deletions

View File

@ -282,6 +282,25 @@ func (n *graphNodeImportStateSub) Execute(ctx EvalContext, op walkOperation) (di
return diags
}
// Verify the existance of the imported resource
if state.Value.IsNull() {
var diags tfdiags.Diagnostics
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Cannot import non-existent remote object",
fmt.Sprintf(
"While attempting to import an existing object to %q, "+
"the provider detected that no object exists with the given id. "+
"Only pre-existing objects can be imported; check that the id "+
"is correct and that it is associated with the provider's "+
"configured region or endpoint, or use \"terraform apply\" to "+
"create a new remote object for this resource.",
n.TargetAddr,
),
))
return diags
}
diags = diags.Append(riNode.writeResourceInstanceState(ctx, state, workingState))
return diags
}

View File

@ -1,6 +1,7 @@
package terraform
import (
"fmt"
"strings"
"testing"
@ -112,3 +113,55 @@ func TestGraphNodeImportStateSubExecute(t *testing.T) {
t.Fatalf("bad state after import: \n%s", actual)
}
}
func TestGraphNodeImportStateSubExecuteNull(t *testing.T) {
state := states.NewState()
provider := testProvider("aws")
provider.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
// return null indicating that the requested resource does not exist
resp.NewState = cty.NullVal(cty.Object(map[string]cty.Type{
"id": cty.String,
}))
return resp
}
ctx := &MockEvalContext{
StateState: state.SyncWrapper(),
ProviderProvider: provider,
ProviderSchemaSchema: &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
},
},
},
},
}
importedResource := providers.ImportedResource{
TypeName: "aws_instance",
State: cty.ObjectVal(map[string]cty.Value{"id": cty.StringVal("bar")}),
}
node := graphNodeImportStateSub{
TargetAddr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
State: importedResource,
ResolvedProvider: addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("aws"),
Module: addrs.RootModule,
},
}
diags := node.Execute(ctx, walkImport)
if !diags.HasErrors() {
t.Fatal("expected error for non-existent resource")
}
fmt.Println(diags.ErrWithWarnings())
}