From c0dbc952360a9588e40980cc0a90600d94ffcbe5 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 15 Jul 2020 19:15:39 -0400 Subject: [PATCH] test destroy with provider depending on a resource --- terraform/context_apply_test.go | 196 +++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 9c3524ebe..752ba4ba6 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -11372,8 +11372,12 @@ output "myoutput" { func TestContext2Apply_scaleInCBD(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` +variable "ct" { + type = number +} + resource "test_instance" "a" { - count = 1 + count = var.ct lifecycle { create_before_destroy = true } @@ -11425,6 +11429,12 @@ output "out" { p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ + Variables: InputValues{ + "ct": &InputValue{ + Value: cty.NumberIntVal(1), + SourceType: ValueFromCaller, + }, + }, Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), @@ -11447,6 +11457,188 @@ output "out" { // check the output, as those can't cause an error planning the value out := state.RootModule().OutputValues["out"].Value.AsString() if out != "a0" { - t.Fatalf(`expected output "new", got: %q`, out) + t.Fatalf(`expected output "a0", got: %q`, out) + } + + // reduce the count to 0 + ctx = testContext2(t, &ContextOpts{ + Variables: InputValues{ + "ct": &InputValue{ + Value: cty.NumberIntVal(0), + SourceType: ValueFromCaller, + }, + }, + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + _, diags = ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + // if resource b isn't going to apply correctly, we will get an error about + // an invalid plan value + state, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + // check the output, as those can't cause an error planning the value + out = state.RootModule().OutputValues["out"].Value.AsString() + if out != "" { + t.Fatalf(`expected output "", got: %q`, out) + } +} + +// Ensure that we can destroy when a provider references a resource that will +// also be destroyed +func TestContext2Apply_destroyProviderReference(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +provider "null" { + value = "" +} + +module "mod" { + source = "./mod" +} + +provider "test" { + value = module.mod.output +} + +resource "test_instance" "bar" { +} +`, + "mod/main.tf": ` +data "null_data_source" "foo" { + count = 1 +} + + +output "output" { + value = data.null_data_source.foo[0].output +} +`}) + + schemaFn := func(name string) *ProviderSchema { + return &ProviderSchema{ + Provider: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": { + Type: cty.String, + Required: true, + }, + }, + }, + ResourceTypes: map[string]*configschema.Block{ + name + "_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + DataSources: map[string]*configschema.Block{ + name + "_data_source": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "output": { + Type: cty.String, + Computed: true, + }, + }, + }, + }, + } + } + + testP := new(MockProvider) + testP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + return providers.ReadResourceResponse{NewState: req.PriorState} + } + testP.GetSchemaReturn = schemaFn("test") + + providerConfig := "" + testP.ConfigureNewFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + value := req.Config.GetAttr("value") + if value.IsKnown() && !value.IsNull() { + providerConfig = value.AsString() + } else { + providerConfig = "" + } + return resp + } + testP.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { + if providerConfig != "valid" { + return nil, fmt.Errorf("provider config is %q", providerConfig) + } + return testApplyFn(info, s, d) + } + testP.DiffFn = testDiffFn + + nullP := new(MockProvider) + nullP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + return providers.ReadResourceResponse{NewState: req.PriorState} + } + nullP.GetSchemaReturn = schemaFn("null") + + nullP.ApplyFn = testApplyFn + nullP.DiffFn = testDiffFn + + nullP.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("ID"), + "output": cty.StringVal("valid"), + }), + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), + addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), + }, + }) + + if _, diags := ctx.Plan(); diags.HasErrors() { + t.Fatalf("plan errors: %s", diags.Err()) + } + + state, diags := ctx.Apply() + if diags.HasErrors() { + t.Fatalf("apply errors: %s", diags.Err()) + } + + ctx = testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), + addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), + }, + + State: state, + Destroy: true, + }) + + if _, diags := ctx.Plan(); diags.HasErrors() { + t.Fatalf("destroy plan errors: %s", diags.Err()) + } + + if _, diags := ctx.Apply(); diags.HasErrors() { + t.Fatalf("destroy apply errors: %s", diags.Err()) } }