core: EvalApply must run to completion even if provider produces errors
Although we have a special case where a result of the wrong type will bail early, we must keep that set of diagnostics separate so that we can still run to completion when there are _already_ diagnostics present (from the provider's response) but the return value _is_ type-conforming. This fix is verified by TestContext2Apply_provisionerCreateFail.
This commit is contained in:
parent
9eb32c4536
commit
ccd1b1df53
|
@ -4378,10 +4378,7 @@ func TestContext2Apply_provisionerCreateFail(t *testing.T) {
|
|||
pr := testProvisioner()
|
||||
p.DiffFn = testDiffFn
|
||||
|
||||
p.ApplyFn = func(
|
||||
info *InstanceInfo,
|
||||
is *InstanceState,
|
||||
id *InstanceDiff) (*InstanceState, error) {
|
||||
p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) {
|
||||
is.ID = "foo"
|
||||
return is, fmt.Errorf("error")
|
||||
}
|
||||
|
@ -4407,10 +4404,10 @@ func TestContext2Apply_provisionerCreateFail(t *testing.T) {
|
|||
t.Fatal("should error")
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(state.String())
|
||||
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
||||
got := strings.TrimSpace(state.String())
|
||||
want := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr)
|
||||
if got != want {
|
||||
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,20 +95,31 @@ func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
|
|||
newVal = cty.NullVal(schema.ImpliedType())
|
||||
}
|
||||
|
||||
var conformDiags tfdiags.Diagnostics
|
||||
for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
conformDiags = conformDiags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid object",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an invalid value after apply for %s. The result could not be saved.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
"Provider %q planned an invalid value after apply for %s. The result cannot not be saved in the Terraform state.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()),
|
||||
),
|
||||
))
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
diags = diags.Append(conformDiags)
|
||||
if conformDiags.HasErrors() {
|
||||
// Bail early in this particular case, because an object that doesn't
|
||||
// conform to the schema can't be saved in the state anyway -- the
|
||||
// serializer will reject it.
|
||||
return nil, diags.Err()
|
||||
}
|
||||
|
||||
// After this point we have a type-conforming result object and so we
|
||||
// must always run to completion to ensure it can be saved. If n.Error
|
||||
// is set then we must not return a non-nil error, in order to allow
|
||||
// evaluation to continue to a later point where our state object will
|
||||
// be saved.
|
||||
|
||||
// By this point there must not be any unknown values remaining in our
|
||||
// object, because we've applied the change and we can't save unknowns
|
||||
// in our persistent state. If any are present then we will indicate an
|
||||
|
|
Loading…
Reference in New Issue