Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package terraform for openSUSE:Factory checked in at 2022-10-08 01:25:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/terraform (Old) and /work/SRC/openSUSE:Factory/.terraform.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "terraform" Sat Oct 8 01:25:51 2022 rev:42 rq:1008744 version:1.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/terraform/terraform.changes 2022-09-30 17:58:50.229382349 +0200 +++ /work/SRC/openSUSE:Factory/.terraform.new.2275/terraform.changes 2022-10-08 01:26:22.790388577 +0200 @@ -1,0 +2,11 @@ +Fri Oct 7 08:55:12 UTC 2022 - Johannes Kastl <ka...@b1-systems.de> + +- update to 1.3.2: + * BUG FIXES: + - Fixed a crash caused by Terraform incorrectly re-registering output value preconditions during the apply phase (rather than just reusing the already-planned checks from the plan phase). (#31890) + - Prevent errors when the provider reports that a deposed instance no longer exists (#31902) + - Using ignore_changes = all could cause persistent diffs with legacy providers (#31914) + - Fix cycles when resource dependencies cross over between independent provider configurations (#31917) + - Improve handling of missing resource instances during import (#31878) + +------------------------------------------------------------------- Old: ---- terraform-1.3.1.obscpio terraform-1.3.1.tar.gz New: ---- terraform-1.3.2.obscpio terraform-1.3.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ terraform.spec ++++++ --- /var/tmp/diff_new_pack.2CdpC3/_old 2022-10-08 01:26:23.942391219 +0200 +++ /var/tmp/diff_new_pack.2CdpC3/_new 2022-10-08 01:26:23.946391228 +0200 @@ -17,7 +17,7 @@ Name: terraform -Version: 1.3.1 +Version: 1.3.2 Release: 0 Summary: Tool for building infrastructure safely and efficiently License: MPL-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.2CdpC3/_old 2022-10-08 01:26:23.986391319 +0200 +++ /var/tmp/diff_new_pack.2CdpC3/_new 2022-10-08 01:26:23.990391328 +0200 @@ -3,8 +3,8 @@ <param name="url">https://github.com/hashicorp/terraform</param> <param name="scm">git</param> <param name="filename">terraform</param> - <param name="versionformat">1.3.1</param> - <param name="revision">v1.3.1</param> + <param name="versionformat">1.3.2</param> + <param name="revision">v1.3.2</param> <param name="exclude">.git</param> </service> <service name="tar" mode="disabled"/> @@ -16,7 +16,7 @@ <param name="basename">terraform</param> </service> <service name="go_modules" mode="disabled"> - <param name="archive">terraform-1.3.1.tar.gz</param> + <param name="archive">terraform-1.3.2.tar.gz</param> </service> </services> ++++++ terraform-1.3.1.obscpio -> terraform-1.3.2.obscpio ++++++ /work/SRC/openSUSE:Factory/terraform/terraform-1.3.1.obscpio /work/SRC/openSUSE:Factory/.terraform.new.2275/terraform-1.3.2.obscpio differ: char 50, line 1 ++++++ terraform-1.3.1.tar.gz -> terraform-1.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/CHANGELOG.md new/terraform-1.3.2/CHANGELOG.md --- old/terraform-1.3.1/CHANGELOG.md 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/CHANGELOG.md 2022-10-06 18:39:19.000000000 +0200 @@ -1,3 +1,13 @@ +## 1.3.2 (October 06, 2022) + +BUG FIXES: + +* Fixed a crash caused by Terraform incorrectly re-registering output value preconditions during the apply phase (rather than just reusing the already-planned checks from the plan phase). ([#31890](https://github.com/hashicorp/terraform/issues/31890)) +* Prevent errors when the provider reports that a deposed instance no longer exists ([#31902](https://github.com/hashicorp/terraform/issues/31902)) +* Using `ignore_changes = all` could cause persistent diffs with legacy providers ([#31914](https://github.com/hashicorp/terraform/issues/31914)) +* Fix cycles when resource dependencies cross over between independent provider configurations ([#31917](https://github.com/hashicorp/terraform/issues/31917)) +* Improve handling of missing resource instances during `import` ([#31878](https://github.com/hashicorp/terraform/issues/31878)) + ## 1.3.1 (September 28, 2022) NOTE: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/context_apply2_test.go new/terraform-1.3.2/internal/terraform/context_apply2_test.go --- old/terraform-1.3.1/internal/terraform/context_apply2_test.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/context_apply2_test.go 2022-10-06 18:39:19.000000000 +0200 @@ -14,11 +14,13 @@ "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" + "github.com/hashicorp/terraform/internal/tfdiags" ) // Test that the PreApply hook is called with the correct deposed key @@ -916,6 +918,119 @@ } }) } + +func TestContext2Apply_outputValuePrecondition(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` + variable "input" { + type = string + } + + module "child" { + source = "./child" + + input = var.input + } + + output "result" { + value = module.child.result + + precondition { + condition = var.input != "" + error_message = "Input must not be empty." + } + } + `, + "child/main.tf": ` + variable "input" { + type = string + } + + output "result" { + value = var.input + + precondition { + condition = var.input != "" + error_message = "Input must not be empty." + } + } + `, + }) + + checkableObjects := []addrs.Checkable{ + addrs.OutputValue{Name: "result"}.Absolute(addrs.RootModuleInstance), + addrs.OutputValue{Name: "result"}.Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)), + } + + t.Run("pass", func(t *testing.T) { + ctx := testContext2(t, &ContextOpts{}) + plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ + Mode: plans.NormalMode, + SetVariables: InputValues{ + "input": &InputValue{ + Value: cty.StringVal("beep"), + SourceType: ValueFromCLIArg, + }, + }, + }) + assertNoDiagnostics(t, diags) + + for _, addr := range checkableObjects { + result := plan.Checks.GetObjectResult(addr) + if result == nil { + t.Fatalf("no check result for %s in the plan", addr) + } + if got, want := result.Status, checks.StatusPass; got != want { + t.Fatalf("wrong check status for %s during planning\ngot: %s\nwant: %s", addr, got, want) + } + } + + state, diags := ctx.Apply(plan, m) + assertNoDiagnostics(t, diags) + for _, addr := range checkableObjects { + result := state.CheckResults.GetObjectResult(addr) + if result == nil { + t.Fatalf("no check result for %s in the final state", addr) + } + if got, want := result.Status, checks.StatusPass; got != want { + t.Errorf("wrong check status for %s after apply\ngot: %s\nwant: %s", addr, got, want) + } + } + }) + + t.Run("fail", func(t *testing.T) { + // NOTE: This test actually catches a failure during planning and so + // cannot proceed to apply, so it's really more of a plan test + // than an apply test but better to keep all of these + // thematically-related test cases together. + ctx := testContext2(t, &ContextOpts{}) + _, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ + Mode: plans.NormalMode, + SetVariables: InputValues{ + "input": &InputValue{ + Value: cty.StringVal(""), + SourceType: ValueFromCLIArg, + }, + }, + }) + if !diags.HasErrors() { + t.Fatalf("succeeded; want error") + } + + const wantSummary = "Module output value precondition failed" + found := false + for _, diag := range diags { + if diag.Severity() == tfdiags.Error && diag.Description().Summary == wantSummary { + found = true + break + } + } + + if !found { + t.Fatalf("missing expected error\nwant summary: %s\ngot: %s", wantSummary, diags.Err().Error()) + } + }) +} func TestContext2Apply_resourceConditionApplyTimeFail(t *testing.T) { // This tests the less common situation where a condition fails due to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/context_import_test.go new/terraform-1.3.2/internal/terraform/context_import_test.go --- old/terraform-1.3.1/internal/terraform/context_import_test.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/context_import_test.go 2022-10-06 18:39:19.000000000 +0200 @@ -915,6 +915,76 @@ } } +// New resources in the config during import won't exist for evaluation +// purposes (until import is upgraded to using a complete plan). This means +// that references to them are unknown, but in the case of single instances, we +// can at least know the type of unknown value. +func TestContextImport_newResourceUnknown(t *testing.T) { + p := testProvider("aws") + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "one" { +} + +resource "test_resource" "two" { + count = length(flatten([test_resource.one.id])) +} + +resource "test_resource" "test" { +} +`}) + + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }) + + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "test_resource", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + }), + }, + }, + } + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + state, diags := ctx.Import(m, states.NewState(), &ImportOpts{ + Targets: []*ImportTarget{ + { + Addr: addrs.RootModuleInstance.ResourceInstance( + addrs.ManagedResourceMode, "test_resource", "test", addrs.NoKey, + ), + ID: "test", + }, + }, + }) + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + ri := state.ResourceInstance(mustResourceInstanceAddr("test_resource.test")) + expected := `{"id":"test"}` + if ri == nil || ri.Current == nil { + t.Fatal("no state is recorded for resource instance test_resource.test") + } + if string(ri.Current.AttrsJSON) != expected { + t.Fatalf("expected %q, got %q\n", expected, ri.Current.AttrsJSON) + } +} + const testImportStr = ` aws_instance.foo: ID = foo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/context_plan2_test.go new/terraform-1.3.2/internal/terraform/context_plan2_test.go --- old/terraform-1.3.1/internal/terraform/context_plan2_test.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/context_plan2_test.go 2022-10-06 18:39:19.000000000 +0200 @@ -3682,3 +3682,148 @@ assertNoErrors(t, diags) } + +// A deposed instances which no longer exists during ReadResource creates NoOp +// change, which should not effect the plan. +func TestContext2Plan_deposedNoLongerExists(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "b" { + count = 1 + test_string = "updated" + lifecycle { + create_before_destroy = true + } +} +`, + }) + + p := simpleMockProvider() + p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + s := req.PriorState.GetAttr("test_string").AsString() + if s == "current" { + resp.NewState = req.PriorState + return resp + } + // pretend the non-current instance has been deleted already + resp.NewState = cty.NullVal(req.PriorState.Type()) + return resp + } + + // Here we introduce a cycle via state which only shows up in the apply + // graph where the actual destroy instances are connected in the graph. + // This could happen for example when a user has an existing state with + // stored dependencies, and changes the config in such a way that + // contradicts the stored dependencies. + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceDeposed( + mustResourceInstanceAddr("test_object.a[0]").Resource, + states.DeposedKey("deposed"), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectTainted, + AttrsJSON: []byte(`{"test_string":"old"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_object.a[0]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectTainted, + AttrsJSON: []byte(`{"test_string":"current"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan(m, state, &PlanOpts{ + Mode: plans.NormalMode, + }) + assertNoErrors(t, diags) +} + +// make sure there are no cycles with changes around a provider configured via +// managed resources. +func TestContext2Plan_destroyWithResourceConfiguredProvider(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { + in = "a" +} + +provider "test" { + alias = "other" + in = test_object.a.out +} + +resource "test_object" "b" { + provider = test.other + in = "a" +} +`}) + + testProvider := &MockProvider{ + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "in": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + ResourceTypes: map[string]providers.Schema{ + "test_object": providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "in": { + Type: cty.String, + Optional: true, + }, + "out": { + Type: cty.Number, + Computed: true, + }, + }, + }, + }, + }, + }, + } + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(testProvider), + }, + }) + + // plan+apply to create the initial state + opts := SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)) + plan, diags := ctx.Plan(m, states.NewState(), opts) + assertNoErrors(t, diags) + state, diags := ctx.Apply(plan, m) + assertNoErrors(t, diags) + + // Resource changes which have dependencies across providers which + // themselves depend on resources can result in cycles. + // Because other_object transitively depends on the module resources + // through its provider, we trigger changes on both sides of this boundary + // to ensure we can create a valid plan. + // + // Try to replace both instances + addrA := mustResourceInstanceAddr("test_object.a") + addrB := mustResourceInstanceAddr(`test_object.b`) + opts.ForceReplace = []addrs.AbsResourceInstance{addrA, addrB} + + _, diags = ctx.Plan(m, state, opts) + assertNoErrors(t, diags) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/context_plan_test.go new/terraform-1.3.2/internal/terraform/context_plan_test.go --- old/terraform-1.3.1/internal/terraform/context_plan_test.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/context_plan_test.go 2022-10-06 18:39:19.000000000 +0200 @@ -6689,6 +6689,70 @@ } } +func TestContext2Plan_legacyProviderIgnoreAll(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + lifecycle { + ignore_changes = all + } + data = "foo" +} +`, + }) + + p := testProvider("test") + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "data": {Type: cty.String, Optional: true}, + }, + }, + }, + }) + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + plan := req.ProposedNewState.AsValueMap() + // Update both the computed id and the configured data. + // Legacy providers expect terraform to be able to ignore these. + + plan["id"] = cty.StringVal("updated") + plan["data"] = cty.StringVal("updated") + resp.PlannedState = cty.ObjectVal(plan) + resp.LegacyTypeSystem = true + return resp + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"orig","data":"orig"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + plan, diags := ctx.Plan(m, state, DefaultPlanOpts) + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + for _, c := range plan.Changes.Resources { + if c.Action != plans.NoOp { + t.Fatalf("expected NoOp plan, got %s\n", c.Action) + } + } +} + func TestContext2Plan_dataRemovalNoProvider(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/evaluate.go new/terraform-1.3.2/internal/terraform/evaluate.go --- old/terraform-1.3.1/internal/terraform/evaluate.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/evaluate.go 2022-10-06 18:39:19.000000000 +0200 @@ -695,6 +695,29 @@ return cty.DynamicVal, diags } + case walkImport: + // Import does not yet plan resource changes, so new resources from + // config are not going to be found here. Once walkImport fully + // plans resources, this case should not longer be needed. + // In the single instance case, we can return a typed unknown value + // for the instance to better satisfy other expressions using the + // value. This of course will not help if statically known + // attributes are expected to be known elsewhere, but reduces the + // number of problematic configs for now. + // Unlike in plan and apply above we can't be sure the count or + // for_each instances are empty, so we return a DynamicVal. We + // don't really have a good value to return otherwise -- empty + // values will fail for direct index expressions, and unknown + // Lists and Maps could fail in some type unifications. + switch { + case config.Count != nil: + return cty.DynamicVal, diags + case config.ForEach != nil: + return cty.DynamicVal, diags + default: + return cty.UnknownVal(ty), diags + } + default: // We should only end up here during the validate walk, // since later walks should have at least partial states populated diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/graph_builder_plan.go new/terraform-1.3.2/internal/terraform/graph_builder_plan.go --- old/terraform-1.3.1/internal/terraform/graph_builder_plan.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/graph_builder_plan.go 2022-10-06 18:39:19.000000000 +0200 @@ -113,6 +113,13 @@ Config: b.Config, RefreshOnly: b.skipPlanChanges, removeRootOutputs: b.Operation == walkPlanDestroy, + + // NOTE: We currently treat anything built with the plan graph + // builder as "planning" for our purposes here, because we share + // the same graph node implementation between all of the walk + // types and so the pre-planning walks still think they are + // producing a plan even though we immediately discard it. + Planning: true, }, // Add orphan resources diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/node_output.go new/terraform-1.3.2/internal/terraform/node_output.go --- old/terraform-1.3.1/internal/terraform/node_output.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/node_output.go 2022-10-06 18:39:19.000000000 +0200 @@ -25,6 +25,14 @@ Config *configs.Output Destroy bool RefreshOnly bool + + // Planning is set to true when this node is in a graph that was produced + // by the plan graph builder, as opposed to the apply graph builder. + // This quirk is just because we share the same node type between both + // phases but in practice there are a few small differences in the actions + // we need to take between plan and apply. See method DynamicExpand for + // details. + Planning bool } var ( @@ -59,9 +67,18 @@ // wants to know the addresses of the checkable objects so that it can // treat them as unknown status if we encounter an error before actually // visiting the checks. + // + // We must do this only during planning, because the apply phase will start + // with all of the same checkable objects that were registered during the + // planning phase. Consumers of our JSON plan and state formats expect + // that the set of checkable objects will be consistent between the plan + // and any state snapshots created during apply, and that only the statuses + // of those objects will have changed. var checkableAddrs addrs.Set[addrs.Checkable] - if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(n.Module)) { - checkableAddrs = addrs.MakeSet[addrs.Checkable]() + if n.Planning { + if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(n.Module)) { + checkableAddrs = addrs.MakeSet[addrs.Checkable]() + } } var g Graph diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/node_resource_abstract_instance.go new/terraform-1.3.2/internal/terraform/node_resource_abstract_instance.go --- old/terraform-1.3.1/internal/terraform/node_resource_abstract_instance.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/node_resource_abstract_instance.go 2022-10-06 18:39:19.000000000 +0200 @@ -881,7 +881,10 @@ // providers that we must accommodate the behavior for now, so for // ignore_changes to work at all on these values, we will revert the // ignored values once more. - plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal, schema) + // A nil schema is passed to processIgnoreChanges to indicate that we + // don't want to fixup a config value according to the schema when + // ignoring "all", rather we are reverting provider imposed changes. + plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal, nil) diags = diags.Append(ignoreChangeDiags) if ignoreChangeDiags.HasErrors() { return plan, state, keyData, diags @@ -1160,6 +1163,14 @@ } if ignoreAll { + // Legacy providers need up to clean up their invalid plans and ensure + // no changes are passed though, but that also means making an invalid + // config with computed values. In that case we just don't supply a + // schema and return the prior val directly. + if schema == nil { + return prior, nil + } + // If we are trying to ignore all attribute changes, we must filter // computed attributes out from the prior state to avoid sending them // to the provider as if they were included in the configuration. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/testdata/import-provider-resources/main.tf new/terraform-1.3.2/internal/terraform/testdata/import-provider-resources/main.tf --- old/terraform-1.3.1/internal/terraform/testdata/import-provider-resources/main.tf 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/testdata/import-provider-resources/main.tf 2022-10-06 18:39:19.000000000 +0200 @@ -1,5 +1,5 @@ provider "aws" { - value = "${test_instance.bar.value}" + value = "${test_instance.bar.id}" } resource "aws_instance" "foo" { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/transform_destroy_edge.go new/terraform-1.3.2/internal/terraform/transform_destroy_edge.go --- old/terraform-1.3.1/internal/terraform/transform_destroy_edge.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/transform_destroy_edge.go 2022-10-06 18:39:19.000000000 +0200 @@ -72,24 +72,41 @@ e := dag.BasicEdge(from, to) g.Connect(e) + // getComparableProvider inspects the node to try and get the most precise + // description of the provider being used to help determine if 2 nodes are + // from the same provider instance. + getComparableProvider := func(pc GraphNodeProviderConsumer) string { + ps := pc.Provider().String() + + // we don't care about `exact` here, since we're only looking for any + // clue that the providers may differ. + p, _ := pc.ProvidedBy() + switch p := p.(type) { + case addrs.AbsProviderConfig: + ps = p.String() + case addrs.LocalProviderConfig: + ps = p.String() + } + + return ps + } + pc, ok := from.(GraphNodeProviderConsumer) if !ok { return } - fromProvider := pc.Provider() + fromProvider := getComparableProvider(pc) pc, ok = to.(GraphNodeProviderConsumer) if !ok { return } - toProvider := pc.Provider() - - sameProvider := fromProvider.Equals(toProvider) + toProvider := getComparableProvider(pc) // Check for cycles, and back out the edge if there are any. // The cycles we are looking for only appears between providers, so don't // waste time checking for cycles if both nodes use the same provider. - if !sameProvider && len(g.Cycles()) > 0 { + if fromProvider != toProvider && len(g.Cycles()) > 0 { log.Printf("[DEBUG] DestroyEdgeTransformer: skipping inter-provider edge %s->%s which creates a cycle", dag.VertexName(from), dag.VertexName(to)) g.RemoveEdge(e) @@ -138,36 +155,29 @@ return nil } - // Connect destroy dependencies as stored in the state - for _, ds := range destroyers { - for _, des := range ds { - ri, ok := des.(GraphNodeResourceInstance) - if !ok { - continue - } + // Go through and connect creators to destroyers. Going along with + // our example, this makes: A_d => A + for _, v := range g.Vertices() { + cn, ok := v.(GraphNodeCreator) + if !ok { + continue + } - for _, resAddr := range ri.StateDependencies() { - for _, desDep := range destroyersByResource[resAddr.String()] { - if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(desDep, des) { - log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(desDep), dag.VertexName(des)) - t.tryInterProviderDestroyEdge(g, desDep, des) - } else { - log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(desDep), dag.VertexName(des)) - } - } + addr := cn.CreateAddr() + if addr == nil { + continue + } - // We can have some create or update nodes which were - // dependents of the destroy node. If they have no destroyer - // themselves, make the connection directly from the creator. - for _, createDep := range creators[resAddr.String()] { - if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(createDep, des) { - log.Printf("[DEBUG] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(createDep), dag.VertexName(des)) - t.tryInterProviderDestroyEdge(g, createDep, des) - } else { - log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(createDep), dag.VertexName(des)) - } - } - } + for _, d := range destroyers[addr.String()] { + // For illustrating our example + a_d := d.(dag.Vertex) + a := v + + log.Printf( + "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q", + dag.VertexName(a), dag.VertexName(a_d)) + + g.Connect(dag.BasicEdge(a, a_d)) } } @@ -192,29 +202,36 @@ } } - // Go through and connect creators to destroyers. Going along with - // our example, this makes: A_d => A - for _, v := range g.Vertices() { - cn, ok := v.(GraphNodeCreator) - if !ok { - continue - } - - addr := cn.CreateAddr() - if addr == nil { - continue - } - - for _, d := range destroyers[addr.String()] { - // For illustrating our example - a_d := d.(dag.Vertex) - a := v + // Connect destroy dependencies as stored in the state + for _, ds := range destroyers { + for _, des := range ds { + ri, ok := des.(GraphNodeResourceInstance) + if !ok { + continue + } - log.Printf( - "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q", - dag.VertexName(a), dag.VertexName(a_d)) + for _, resAddr := range ri.StateDependencies() { + for _, desDep := range destroyersByResource[resAddr.String()] { + if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(desDep, des) { + log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(desDep), dag.VertexName(des)) + t.tryInterProviderDestroyEdge(g, desDep, des) + } else { + log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(desDep), dag.VertexName(des)) + } + } - g.Connect(dag.BasicEdge(a, a_d)) + // We can have some create or update nodes which were + // dependents of the destroy node. If they have no destroyer + // themselves, make the connection directly from the creator. + for _, createDep := range creators[resAddr.String()] { + if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(createDep, des) { + log.Printf("[DEBUG] DestroyEdgeTransformer2: %s has stored dependency of %s\n", dag.VertexName(createDep), dag.VertexName(des)) + t.tryInterProviderDestroyEdge(g, createDep, des) + } else { + log.Printf("[TRACE] DestroyEdgeTransformer2: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(createDep), dag.VertexName(des)) + } + } + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/transform_diff.go new/terraform-1.3.2/internal/terraform/transform_diff.go --- old/terraform-1.3.1/internal/terraform/transform_diff.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/transform_diff.go 2022-10-06 18:39:19.000000000 +0200 @@ -80,7 +80,10 @@ update = true } - if dk != states.NotDeposed && update { + // A deposed instance may only have a change of Delete or NoOp. A NoOp + // can happen if the provider shows it no longer exists during the most + // recent ReadResource operation. + if dk != states.NotDeposed && !(rc.Action == plans.Delete || rc.Action == plans.NoOp) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Invalid planned change for deposed object", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/internal/terraform/transform_output.go new/terraform-1.3.2/internal/terraform/transform_output.go --- old/terraform-1.3.1/internal/terraform/transform_output.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/internal/terraform/transform_output.go 2022-10-06 18:39:19.000000000 +0200 @@ -26,6 +26,10 @@ // Refresh-only mode means that any failing output preconditions are // reported as warnings rather than errors RefreshOnly bool + + // Planning must be set to true only when we're building a planning graph. + // It must be set to false whenever we're building an apply graph. + Planning bool } func (t *OutputTransformer) Transform(g *Graph) error { @@ -89,6 +93,7 @@ Config: o, Destroy: t.removeRootOutputs, RefreshOnly: t.RefreshOnly, + Planning: t.Planning, } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/version/version.go new/terraform-1.3.2/version/version.go --- old/terraform-1.3.1/version/version.go 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/version/version.go 2022-10-06 18:39:19.000000000 +0200 @@ -11,7 +11,7 @@ ) // The main version number that is being run at the moment. -var Version = "1.3.1" +var Version = "1.3.2" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/website/docs/language/expressions/custom-conditions.mdx new/terraform-1.3.2/website/docs/language/expressions/custom-conditions.mdx --- old/terraform-1.3.1/website/docs/language/expressions/custom-conditions.mdx 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/website/docs/language/expressions/custom-conditions.mdx 2022-10-06 18:39:19.000000000 +0200 @@ -102,6 +102,10 @@ Terraform evaluates output value preconditions before evaluating the `value` expression to finalize the result. Preconditions can take precedence over potential errors in the `value` expression. +### Continuous Validation in Terraform Cloud + +Terraform Cloud can automatically check whether the preconditions and postconditions in a workspace???s configuration continue to pass after Terraform provisions the infrastructure. For example, you can write a `postcondition` to check whether an API gateway certificate is valid. Continuous validation alerts you when the condition fails, so you can update the certificate and avoid errors the next time you want to update your infrastructure. Refer to [Continuous Validation](/cloud-docs/workspaces/health#continuous-validation) in the Terraform Cloud documentation for details. + ### Examples The following example shows use cases for preconditions and postconditions. The preconditions and postconditions declare the following assumptions and guarantees. @@ -115,9 +119,18 @@ ```hcl +data "aws_ami" "example" { + owners = ["amazon"] + + filter { + name = "image-id" + values = ["ami-abc123"] + } +} + resource "aws_instance" "example" { - instance_type = "t2.micro" - ami = "ami-abc123" + instance_type = "t3.micro" + ami = data.aws_ami.example.id lifecycle { # The AMI ID must refer to an AMI that contains an operating system diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/website/docs/language/modules/develop/index.mdx new/terraform-1.3.2/website/docs/language/modules/develop/index.mdx --- old/terraform-1.3.1/website/docs/language/modules/develop/index.mdx 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/website/docs/language/modules/develop/index.mdx 2022-10-06 18:39:19.000000000 +0200 @@ -70,6 +70,12 @@ adding unnecessary complexity. Just use the resource type directly in the calling module instead. +### No-Code Provisioning in Terraform Cloud + +You can also create no-code ready modules to enable the no-code provisioning workflow in Terraform Cloud. No-code provisioning lets users deploy a module's resources in Terraform Cloud without writing any Terraform configuration. + +No-code ready modules have additional requirements and considerations. Refer to [Designing No-Code Ready Modules](/cloud-docs/no-code-provisioning/module-design) in the Terraform Cloud documentation for details. + ## Refactoring module resources You can include [refactoring blocks](/language/modules/develop/refactoring) to record how resource diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/terraform-1.3.1/website/docs/language/state/backends.mdx new/terraform-1.3.2/website/docs/language/state/backends.mdx --- old/terraform-1.3.1/website/docs/language/state/backends.mdx 2022-09-28 15:46:33.000000000 +0200 +++ new/terraform-1.3.2/website/docs/language/state/backends.mdx 2022-10-06 18:39:19.000000000 +0200 @@ -66,9 +66,7 @@ Backends are responsible for supporting [state locking](/language/state/locking) if possible. -Not all backends support locking. The -[documentation for each backend](/language/settings/backends) -includes details on whether it supports locking or not. +Not all backends support locking. The [documentation for each backend](/language/settings/backends/configuration#available-backends) includes details about whether it supports locking or not. For more information on state locking, view the [page dedicated to state locking](/language/state/locking). ++++++ terraform.obsinfo ++++++ --- /var/tmp/diff_new_pack.2CdpC3/_old 2022-10-08 01:26:25.066393797 +0200 +++ /var/tmp/diff_new_pack.2CdpC3/_new 2022-10-08 01:26:25.066393797 +0200 @@ -1,5 +1,5 @@ name: terraform -version: 1.3.1 -mtime: 1664372793 -commit: ebc1f295cafb53341f9b58c3c16099f98d3b58a6 +version: 1.3.2 +mtime: 1665074359 +commit: 72723ea9ca27e337cea3afc73fa15cfa138c0105 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/terraform/vendor.tar.gz /work/SRC/openSUSE:Factory/.terraform.new.2275/vendor.tar.gz differ: char 5, line 1