Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package opentofu for openSUSE:Factory checked in at 2026-04-18 21:37:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/opentofu (Old) and /work/SRC/openSUSE:Factory/.opentofu.new.11940 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "opentofu" Sat Apr 18 21:37:00 2026 rev:45 rq:1347918 version:1.11.6 Changes: -------- --- /work/SRC/openSUSE:Factory/opentofu/opentofu.changes 2026-02-13 12:49:55.278284053 +0100 +++ /work/SRC/openSUSE:Factory/.opentofu.new.11940/opentofu.changes 2026-04-18 21:37:25.964184710 +0200 @@ -1,0 +2,25 @@ +Thu Apr 09 08:07:10 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.11.6: + * BUG FIXES: + - Running tofu apply -refresh-only with a configuration that + contains ephemeral resources does not fail anymore because + the refresh produced changes (#3776) + - Fixed tofu init crashing when a module version uses a + variable and the module is referenced from a test file. + (#3686) + - Fixed provider-defined functions in import block id + expressions causing "BUG: Uninitialized function provider" + error. (#3803) + - tofu test no longer fails during cleanup when using a mocked + version of a resource type with write-only attributes. + (#3964) + - A malicious remote TLS server can no longer deadlock OpenTofu + by sending multiple key update messages in a single record. + (#3966) + - When installing module packages from "tar" archives, OpenTofu + now accepts only a limited number of sparse file entries to + avoid unbounded memory usage from maliciously-crafted + archives containing many sparse regions. (#3966) + +------------------------------------------------------------------- Old: ---- opentofu-1.11.5.obscpio New: ---- opentofu-1.11.6.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ opentofu.spec ++++++ --- /var/tmp/diff_new_pack.sBjL12/_old 2026-04-18 21:37:27.008227939 +0200 +++ /var/tmp/diff_new_pack.sBjL12/_new 2026-04-18 21:37:27.012228104 +0200 @@ -19,7 +19,7 @@ %define executable_name tofu Name: opentofu -Version: 1.11.5 +Version: 1.11.6 Release: 0 Summary: Declaratively manage your cloud infrastructure License: MPL-2.0 @@ -29,7 +29,7 @@ Source1: vendor.tar.gz Source99: opentofu-rpmlintrc BuildRequires: bash-completion -BuildRequires: go1.25 >= 1.25.6 +BuildRequires: go1.25 >= 1.25.9 BuildRequires: golang-packaging # See: https://github.com/hashicorp/opentofu/issues/22807 ExcludeArch: %{ix86} %{arm} ++++++ _service ++++++ --- /var/tmp/diff_new_pack.sBjL12/_old 2026-04-18 21:37:27.044229429 +0200 +++ /var/tmp/diff_new_pack.sBjL12/_new 2026-04-18 21:37:27.048229595 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/opentofu/opentofu/</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v1.11.5</param> + <param name="revision">v1.11.6</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.sBjL12/_old 2026-04-18 21:37:27.068230423 +0200 +++ /var/tmp/diff_new_pack.sBjL12/_new 2026-04-18 21:37:27.076230754 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/opentofu/opentofu/</param> - <param name="changesrevision">67fe9db49b7dafd46470cf9ac7f437aaa95f5c40</param></service></servicedata> + <param name="changesrevision">698e5e5204756963ffb28107fb61d77970e47f66</param></service></servicedata> (No newline at EOF) ++++++ opentofu-1.11.5.obscpio -> opentofu-1.11.6.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/.go-version new/opentofu-1.11.6/.go-version --- old/opentofu-1.11.5/.go-version 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/.go-version 2026-04-08 12:54:56.000000000 +0200 @@ -1 +1 @@ -1.25.6 +1.25.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/CHANGELOG.md new/opentofu-1.11.6/CHANGELOG.md --- old/opentofu-1.11.5/CHANGELOG.md 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/CHANGELOG.md 2026-04-08 12:54:56.000000000 +0200 @@ -1,5 +1,17 @@ The v1.11.x release series is supported until **August 1 2026**. -## 1.11.6 (Unreleased) + +## 1.11.7 (Unreleased) + +## 1.11.6 + +BUG FIXES: + +* Running `tofu apply -refresh-only` with a configuration that contains ephemeral resources does not fail anymore because the refresh produced changes ([#3776](https://github.com/opentofu/opentofu/issues/3776)) +* Fixed `tofu init` crashing when a module `version` uses a variable and the module is referenced from a test file. ([#3686](https://github.com/opentofu/opentofu/issues/3686)) +* Fixed provider-defined functions in `import` block `id` expressions causing "BUG: Uninitialized function provider" error. ([#3803](https://github.com/opentofu/opentofu/issues/3803)) +* `tofu test` no longer fails during cleanup when using a mocked version of a resource type with write-only attributes. ([#3964](https://github.com/opentofu/opentofu/issues/3964)) +* A malicious remote TLS server can no longer deadlock OpenTofu by sending multiple key update messages in a single record. ([#3966](https://github.com/opentofu/opentofu/pull/3966)) +* When installing module packages from "tar" archives, OpenTofu now accepts only a limited number of sparse file entries to avoid unbounded memory usage from maliciously-crafted archives containing many sparse regions. ([#3966](https://github.com/opentofu/opentofu/pull/3966)) ## 1.11.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/go.mod new/opentofu-1.11.6/go.mod --- old/opentofu-1.11.5/go.mod 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/go.mod 2026-04-08 12:54:56.000000000 +0200 @@ -1,6 +1,6 @@ module github.com/opentofu/opentofu -go 1.25.6 +go 1.25.9 // At the time of adding this configuration, the new Go feature introduced here https://github.com/golang/go/issues/67061, // was having a good amount of issues linked to, affecting AWS Firewall, GCP various services and a lot more. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/configs/config_build.go new/opentofu-1.11.6/internal/configs/config_build.go --- old/opentofu-1.11.5/internal/configs/config_build.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/configs/config_build.go 2026-04-08 12:54:56.000000000 +0200 @@ -14,6 +14,7 @@ version "github.com/hashicorp/go-version" "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" "github.com/opentofu/opentofu/internal/addrs" ) @@ -81,7 +82,27 @@ SourceAddrRange: run.Module.SourceDeclRange, VersionConstraint: run.Module.Version, Parent: root, - CallRange: run.Module.DeclRange, + Call: NewStaticModuleCall(path, func(v *Variable) (cty.Value, hcl.Diagnostics) { + // Handle the case where this is overridden in the test run block + expr, isOverridden := run.Variables[v.Name] + if isOverridden { + identifier := StaticIdentifier{ + Module: path, + Subject: fmt.Sprintf("var.%s", v.Name), + DeclRange: expr.Range(), + } + return root.Module.StaticEvaluator.Evaluate(ctx, expr, identifier) + } + + // If we haven't had it overridden in a run block, fall back to trying our best + // but we do have defaults for some that we can use. + if v.Default != cty.NilVal { + return v.Default, nil + } + return cty.DynamicVal, nil + }, root.Module.SourceDir, root.Module.StaticEvaluator.call.workspace), + + CallRange: run.Module.DeclRange, } cfg, modDiags := loadModule(ctx, root, &req, walker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/configs/hcl2shim/mock_value_composer.go new/opentofu-1.11.6/internal/configs/hcl2shim/mock_value_composer.go --- old/opentofu-1.11.5/internal/configs/hcl2shim/mock_value_composer.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/configs/hcl2shim/mock_value_composer.go 2026-04-08 12:54:56.000000000 +0200 @@ -74,7 +74,9 @@ } /* -composeMockValueForAttributes follows the truth table below for generating an output value, given schema and inputs: +composeMockValueForAttributes generates a new mocked value based on the given schema and inputs. +For write-only attributes, it always returns null value based on the attribute's schema. +For all other attribute configurations, the generated value follows the truth table below: Required Optional Computed Has config Has Override Result t f f t t Error - Not Allowed to override config t f f t f Config @@ -148,7 +150,16 @@ } // Determine the value - if attr.Required { + if attr.WriteOnly { + mockAttrs[k] = cty.NullVal(attr.ImpliedType()) + if hasOverride { + diags = diags.Append(tfdiags.WholeContainingBody( + tfdiags.Warning, + fmt.Sprintf("Encountered override for write-only attribute `%v`", k), + "Override values for write-only attributes have no impact since the write-only attributes will always be null in the provider response.", + )) + } + } else if attr.Required { // Value from configuration only if !hasConfig { diags = diags.Append(tfdiags.WholeContainingBody( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/configs/hcl2shim/mock_value_composer_test.go new/opentofu-1.11.6/internal/configs/hcl2shim/mock_value_composer_test.go --- old/opentofu-1.11.5/internal/configs/hcl2shim/mock_value_composer_test.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/configs/hcl2shim/mock_value_composer_test.go 2026-04-08 12:54:56.000000000 +0200 @@ -3,6 +3,7 @@ import ( "testing" + "github.com/opentofu/opentofu/internal/tfdiags" "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" @@ -21,6 +22,7 @@ defaults map[string]cty.Value wantVal cty.Value wantError bool + wantWarn bool }{ "diff-props-in-root-attributes": { schema: &configschema.Block{ @@ -74,20 +76,39 @@ Computed: true, Sensitive: true, }, + "write-only-required": { + Type: cty.String, + Required: true, + Optional: false, + Computed: false, + Sensitive: true, + WriteOnly: true, + }, + "write-only-computed": { + Type: cty.String, + Required: false, + Optional: false, + Computed: true, + Sensitive: true, + WriteOnly: true, + }, }, }, config: cty.ObjectVal(map[string]cty.Value{ - "required-only": cty.StringVal("required"), - "sensitive-required": cty.StringVal("sensitive"), + "required-only": cty.StringVal("required"), + "sensitive-required": cty.StringVal("sensitive"), + "write-only-required": cty.StringVal("write-only-value"), }), wantVal: cty.ObjectVal(map[string]cty.Value{ - "required-only": cty.StringVal("required"), - "optional": cty.NullVal(cty.String), - "optional-computed": cty.StringVal("6zQu0"), - "computed-only": cty.StringVal("l3INvNSQT"), - "sensitive-optional": cty.NullVal(cty.String), - "sensitive-required": cty.StringVal("sensitive"), - "sensitive-computed": cty.StringVal("xNmGyAVmNkB4"), + "required-only": cty.StringVal("required"), + "optional": cty.NullVal(cty.String), + "optional-computed": cty.StringVal("6zQu0"), + "computed-only": cty.StringVal("l3INvNSQT"), + "sensitive-optional": cty.NullVal(cty.String), + "sensitive-required": cty.StringVal("sensitive"), + "sensitive-computed": cty.StringVal("xNmGyAVmNkB4"), + "write-only-required": cty.NullVal(cty.String), + "write-only-computed": cty.NullVal(cty.String), }), }, "diff-props-in-single-block-attributes": { @@ -146,6 +167,22 @@ Computed: true, Sensitive: true, }, + "write-only-required": { + Type: cty.String, + Required: true, + Optional: false, + Computed: false, + Sensitive: true, + WriteOnly: true, + }, + "write-only-computed": { + Type: cty.String, + Required: false, + Optional: false, + Computed: true, + Sensitive: true, + WriteOnly: true, + }, }, }, }, @@ -153,19 +190,22 @@ }, config: cty.ObjectVal(map[string]cty.Value{ "nested": cty.ObjectVal(map[string]cty.Value{ - "required-only": cty.StringVal("required"), - "sensitive-required": cty.StringVal("sensitive"), + "required-only": cty.StringVal("required"), + "sensitive-required": cty.StringVal("sensitive"), + "write-only-required": cty.StringVal("write-only-value"), }), }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.ObjectVal(map[string]cty.Value{ - "required-only": cty.StringVal("required"), - "optional": cty.NullVal(cty.String), - "optional-computed": cty.StringVal("6zQu0"), - "computed-only": cty.StringVal("l3INvNSQT"), - "sensitive-optional": cty.NullVal(cty.String), - "sensitive-required": cty.StringVal("sensitive"), - "sensitive-computed": cty.StringVal("xNmGyAVmNkB4"), + "required-only": cty.StringVal("required"), + "optional": cty.NullVal(cty.String), + "optional-computed": cty.StringVal("6zQu0"), + "computed-only": cty.StringVal("l3INvNSQT"), + "sensitive-optional": cty.NullVal(cty.String), + "sensitive-required": cty.StringVal("sensitive"), + "sensitive-computed": cty.StringVal("xNmGyAVmNkB4"), + "write-only-required": cty.NullVal(cty.String), + "write-only-computed": cty.NullVal(cty.String), }), }), }, @@ -180,6 +220,11 @@ Type: cty.Number, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, Optional: true, @@ -191,7 +236,8 @@ }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.NullVal(cty.Object(map[string]cty.Type{ - "attr": cty.Number, + "attr": cty.Number, + "write-only": cty.String, })), }), }, @@ -228,6 +274,11 @@ Type: cty.Number, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, Optional: true, @@ -239,7 +290,8 @@ }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ - "attr": cty.Number, + "attr": cty.Number, + "write-only": cty.String, }))), }), }, @@ -254,6 +306,11 @@ Type: cty.Number, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, Optional: true, @@ -265,7 +322,8 @@ }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ - "attr": cty.Number, + "attr": cty.Number, + "write-only": cty.String, }))), }), }, @@ -280,6 +338,11 @@ Type: cty.Number, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, Optional: true, @@ -291,7 +354,8 @@ }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "attr": cty.Number, + "attr": cty.Number, + "write-only": cty.String, }))), }), }, @@ -306,6 +370,11 @@ Type: cty.Number, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, }, @@ -316,7 +385,8 @@ }), wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.ObjectVal(map[string]cty.Value{ - "field": cty.NumberIntVal(0), + "field": cty.NumberIntVal(0), + "write-only": cty.NullVal(cty.String), }), }), }, @@ -339,6 +409,11 @@ Type: cty.String, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, }, @@ -350,9 +425,10 @@ wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ - "num": cty.NumberIntVal(0), - "str1": cty.StringVal("l3INvNSQT"), - "str2": cty.StringVal("6zQu0"), + "num": cty.NumberIntVal(0), + "str1": cty.StringVal("l3INvNSQT"), + "str2": cty.StringVal("6zQu0"), + "write-only": cty.NullVal(cty.String), }), }), }), @@ -376,6 +452,11 @@ Type: cty.String, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, }, @@ -387,9 +468,10 @@ wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ - "num": cty.NumberIntVal(0), - "str1": cty.StringVal("l3INvNSQT"), - "str2": cty.StringVal("6zQu0"), + "num": cty.NumberIntVal(0), + "str1": cty.StringVal("l3INvNSQT"), + "str2": cty.StringVal("6zQu0"), + "write-only": cty.NullVal(cty.String), }), }), }), @@ -413,6 +495,11 @@ Type: cty.String, Computed: true, }, + "write-only": { + Type: cty.String, + Computed: true, + WriteOnly: true, + }, }, }, }, @@ -426,9 +513,10 @@ wantVal: cty.ObjectVal(map[string]cty.Value{ "nested": cty.MapVal(map[string]cty.Value{ "somelabel": cty.ObjectVal(map[string]cty.Value{ - "num": cty.NumberIntVal(0), - "str1": cty.StringVal("l3INvNSQT"), - "str2": cty.StringVal("6zQu0"), + "num": cty.NumberIntVal(0), + "str1": cty.StringVal("l3INvNSQT"), + "str2": cty.StringVal("6zQu0"), + "write-only": cty.NullVal(cty.String), }), }), }), @@ -504,6 +592,12 @@ Computed: true, Optional: true, }, + "write-only": { + Type: cty.String, + Computed: true, + Optional: true, + WriteOnly: true, + }, }, }, }, @@ -532,7 +626,8 @@ "fieldNum": cty.NumberIntVal(0), "fieldStr": cty.StringVal("ionwj3qrsh4xyC9"), }), - "list": cty.ListValEmpty(cty.String), + "list": cty.ListValEmpty(cty.String), + "write-only": cty.NullVal(cty.String), }), }), }), @@ -576,6 +671,12 @@ Computed: true, Optional: true, }, + "write-only": { + Type: cty.String, + Computed: true, + Optional: true, + WriteOnly: true, + }, }, }, }, @@ -588,6 +689,7 @@ "useConfigValue": cty.StringVal("iAmFromConfig"), }), }), + "write-only": cty.StringVal("iAmFromConfig"), }), defaults: map[string]cty.Value{ "useDefaultsValue": cty.StringVal("iAmFromDefaults"), @@ -604,6 +706,7 @@ "useConfigValue": cty.StringVal("iAmFromConfig"), "useDefaultsValue": cty.StringVal("iAmFromDefaults"), "generateMockValue": cty.StringVal("6zQu0"), + "write-only": cty.NullVal(cty.String), }), }), }), @@ -646,6 +749,25 @@ }, wantError: true, }, + "override-of-write-only-without-config": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "write-only": { + Type: cty.String, + Optional: true, + Computed: true, + WriteOnly: true, + }, + }, + }, + defaults: map[string]cty.Value{ + "write-only": cty.StringVal("str"), + }, + wantVal: cty.ObjectVal(map[string]cty.Value{ + "write-only": cty.NullVal(cty.String), + }), + wantWarn: true, + }, "dynamically-typed-values": { schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -675,9 +797,24 @@ case !test.wantError && gotDiags.HasErrors(): t.Fatalf("Got unexpected error diags: %v", gotDiags.ErrWithWarnings()) + case test.wantWarn && !hasWarns(gotDiags): + t.Fatalf("Expected warns in diags, but none returned") + + case !test.wantWarn && hasWarns(gotDiags): + t.Fatalf("Got unexpected warn diags: %v", gotDiags.ErrWithWarnings()) + case !test.wantVal.RawEquals(gotVal): t.Fatalf("Wrong value\ngot: %swant: %sdiff: %s", ctydebug.ValueString(gotVal), ctydebug.ValueString(test.wantVal), ctydebug.DiffValues(test.wantVal, gotVal)) } }) } } + +func hasWarns(diags tfdiags.Diagnostics) bool { + for _, diag := range diags { + if diag.Severity() == tfdiags.Warning { + return true + } + } + return false +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/instances/expander.go new/opentofu-1.11.6/internal/instances/expander.go --- old/opentofu-1.11.5/internal/instances/expander.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/instances/expander.go 2026-04-08 12:54:56.000000000 +0200 @@ -156,6 +156,9 @@ // expansion registered using one of the SetModule* methods before calling, // or this method will panic. func (e *Expander) GetDeepestExistingModuleInstance(given addrs.ModuleInstance) addrs.ModuleInstance { + e.mu.RLock() + defer e.mu.RUnlock() + exps := e.exps // start with the root module expansions for i := 0; i < len(given); i++ { step := given[i] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/plans/changes.go new/opentofu-1.11.6/internal/plans/changes.go --- old/opentofu-1.11.5/internal/plans/changes.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/plans/changes.go 2026-04-08 12:54:56.000000000 +0200 @@ -73,6 +73,23 @@ return true } +// ActionableResources returns all the [Changes.Resources] that are changes that would actually +// update the resources. +// This method's main purpose is to exclude from [Changes.Resources] the changes that are +// in the plan strictly for building the graph and are not going to change the resource state. +// In case of [Open] actions, these are needed to build the required ephemeral nodes +// in [DiffTransformer]. +func (c *Changes) ActionableResources() []*ResourceInstanceChangeSrc { + var ret []*ResourceInstanceChangeSrc + for _, r := range c.Resources { + if r.Action == Open { + continue + } + ret = append(ret, r) + } + return ret +} + // ResourceInstance returns the planned change for the current object of the // resource instance of the given address, if any. Returns nil if no change is // planned. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/tofu/context_plan.go new/opentofu-1.11.6/internal/tofu/context_plan.go --- old/opentofu-1.11.5/internal/tofu/context_plan.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/tofu/context_plan.go 2026-04-08 12:54:56.000000000 +0200 @@ -387,10 +387,13 @@ // to refresh only, the set of resource changes should always be empty. // We'll safety-check that here so we can return a clear message about it, // rather than probably just generating confusing output at the UI layer. - if len(plan.Changes.Resources) != 0 { + // Because the ephemeral resources changes in the plan are meant to be used + // later to build the apply graph, those shouldn't be counted when we are + // doing this check. + if changes := plan.Changes.ActionableResources(); len(changes) != 0 { // Some extra context in the logs in case the user reports this message // as a bug, as a starting point for debugging. - for _, rc := range plan.Changes.Resources { + for _, rc := range changes { if depKey := rc.DeposedKey; depKey == states.NotDeposed { log.Printf("[DEBUG] Refresh-only plan includes %s change for %s", rc.Action, rc.Addr) } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/tofu/context_plan2_test.go new/opentofu-1.11.6/internal/tofu/context_plan2_test.go --- old/opentofu-1.11.5/internal/tofu/context_plan2_test.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/tofu/context_plan2_test.go 2026-04-08 12:54:56.000000000 +0200 @@ -20,11 +20,11 @@ "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" + "github.com/opentofu/opentofu/internal/addrs" "github.com/opentofu/opentofu/internal/checks" "github.com/opentofu/opentofu/internal/configs" - "github.com/zclconf/go-cty/cty" - "github.com/opentofu/opentofu/internal/configs/configschema" "github.com/opentofu/opentofu/internal/lang/marks" "github.com/opentofu/opentofu/internal/plans" @@ -2683,6 +2683,94 @@ } } +func TestContext2Plan_refreshOnlyMode_ephemeral(t *testing.T) { + addr := mustResourceInstanceAddr("ephemeral.test_object.a") + + // The configuration, the prior state, and the refresh result intentionally + // have different values for "test_string" so we can observe that the + // refresh took effect but the configuration change wasn't considered. + m := testModuleInline(t, map[string]string{ + "main.tf": ` + ephemeral "test_object" "a" { + arg = "after" + } + `, + }) + state := states.NewState() + + p := simpleMockProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + EphemeralResources: map[string]providers.Schema{ + "test_object": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "arg": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + p.OpenEphemeralResourceFn = func(req providers.OpenEphemeralResourceRequest) providers.OpenEphemeralResourceResponse { + newVal, err := cty.Transform(req.Config, func(path cty.Path, v cty.Value) (cty.Value, error) { + if len(path) == 1 && path[0] == (cty.GetAttrStep{Name: "arg"}) { + return cty.StringVal("current"), nil + } + return v, nil + }) + if err != nil { + // shouldn't get here + t.Fatalf("OpenResourceFn transform failed") + return providers.OpenEphemeralResourceResponse{} + } + return providers.OpenEphemeralResourceResponse{ + Result: newVal, + } + } + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan(context.Background(), m, state, &PlanOpts{ + Mode: plans.RefreshOnlyMode, + }) + if diags.HasErrors() { + t.Fatalf("unexpected errors\n%s", diags.Err().Error()) + } + + if !p.OpenEphemeralResourceCalled { + t.Errorf("Provider's OpenEphemeralResource wasn't called; should've been") + } + + if got, want := len(plan.Changes.Resources), 1; got != want { + t.Fatalf("expected to have exactly %d resource but got %d", want, got) + } + if gotResAddr := plan.Changes.Resources[0].Addr; !gotResAddr.Equal(addr) { + t.Errorf("plan contains one resource and that's NOT an ephemeral as expected; instead, got %s", gotResAddr) + } + if got, want := len(plan.Changes.ActionableResources()), 0; got != want { + t.Errorf( + "changes.ActionableResources() returned more than %d resources, meaning that didn't exclude ephemeral resources. Instead returned %d\nChanges:\n%s", + want, + got, + spew.Sdump(plan.Changes.Resources), + ) + } + + if instState := plan.PlannedState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no planned state, but it should have since it's needed to build the apply graph correctly", addr) + } else { + want := `{"arg":"current"}` + got := string(instState.Current.AttrsJSON) + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("unexpected attributes for the planned ephemeral:\n%s", diff) + } + } +} + func TestContext2Plan_refreshOnlyMode_deposed(t *testing.T) { addr := mustResourceInstanceAddr("test_object.a") deposedKey := states.DeposedKey("byebye") @@ -5248,6 +5336,64 @@ } } +func TestContext2Plan_importUsingProviderDefinedFunction(t *testing.T) { + p := testProvider("aws") + + p.GetProviderSchemaResponse.Functions = map[string]providers.FunctionSpec{ + "echo": { + Parameters: []providers.FunctionParameterSpec{{ + Name: "input", + Type: cty.String, + }}, + Return: cty.String, + }, + } + + p.CallFunctionResponse = &providers.CallFunctionResponse{ + Result: cty.StringVal("foo"), + } + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + required_providers { + aws = {} + } +} + +import { + to = aws_instance.foo + id = provider::aws::echo("foo") +} + +resource "aws_instance" "foo" { +} +`, + }) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + }) + + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, + }, + } + + _, diags := ctx.Plan(t.Context(), m, states.NewState(), DefaultPlanOpts) + if diags.HasErrors() { + t.Fatalf("unexpected errors: %s", diags.Err()) + } +} + func TestContext2Plan_importToDynamicAddress(t *testing.T) { type TestConfiguration struct { Description string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/tofu/transform_provider.go new/opentofu-1.11.6/internal/tofu/transform_provider.go --- old/opentofu-1.11.5/internal/tofu/transform_provider.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/tofu/transform_provider.go 2026-04-08 12:54:56.000000000 +0200 @@ -11,6 +11,7 @@ "log" "github.com/hashicorp/hcl/v2" + "github.com/opentofu/opentofu/internal/addrs" "github.com/opentofu/opentofu/internal/configs" "github.com/opentofu/opentofu/internal/dag" @@ -340,16 +341,41 @@ // LuT of provider reference -> provider vertex providerReferences := make(map[ProviderFunctionReference]dag.Vertex) + type NodeReference struct { + ref *addrs.Reference + module addrs.Module + } + for _, v := range g.Vertices() { // Provider function references if nr, ok := v.(GraphNodeReferencer); ok && t.Config != nil { + + // Construct the set of references that we need to check. + var refs []NodeReference + + // We collate both References() and RootReferences() because + // import block expressions are returned via RootReferences(), + // since they are always evaluated in the root module context + // regardless of whether the import target is in a child module. for _, ref := range nr.References() { - if pf, ok := ref.Subject.(addrs.ProviderFunction); ok { - refPath := nr.ModulePath() + refPath := nr.ModulePath() + if outside, isOutside := v.(GraphNodeReferenceOutside); isOutside { + _, refPath = outside.ReferenceOutside() + } + refs = append(refs, NodeReference{ref, refPath}) + } - if outside, isOutside := v.(GraphNodeReferenceOutside); isOutside { - _, refPath = outside.ReferenceOutside() - } + if nr, ok := v.(GraphNodeRootReferencer); ok { + for _, ref := range nr.RootReferences() { + refs = append(refs, NodeReference{ref, addrs.RootModule}) + } + } + // Now that we have a set of the references, Let's iterate over them + for _, nodeRef := range refs { + ref := nodeRef.ref + + if pf, ok := ref.Subject.(addrs.ProviderFunction); ok { + refPath := nodeRef.module key := ProviderFunctionReference{ ModulePath: refPath.String(), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/internal/tofu/transform_provider_test.go new/opentofu-1.11.6/internal/tofu/transform_provider_test.go --- old/opentofu-1.11.5/internal/tofu/transform_provider_test.go 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/internal/tofu/transform_provider_test.go 2026-04-08 12:54:56.000000000 +0200 @@ -12,17 +12,18 @@ "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty/cty" + "github.com/opentofu/opentofu/internal/addrs" "github.com/opentofu/opentofu/internal/configs" "github.com/opentofu/opentofu/internal/dag" - "github.com/zclconf/go-cty/cty" ) -func testProviderTransformerGraph(t *testing.T, cfg *configs.Config) *Graph { +func testProviderTransformerGraph(t *testing.T, cfg *configs.Config, importTargets ...*ImportTarget) *Graph { t.Helper() g := &Graph{Path: addrs.RootModuleInstance} - ct := &ConfigTransformer{Config: cfg} + ct := &ConfigTransformer{Config: cfg, importTargets: importTargets} if err := ct.Transform(t.Context(), g); err != nil { t.Fatal(err) } @@ -514,7 +515,8 @@ output "output_test" { value = provider::aws::arn_build("aws", "s3", "", "", "test") } -`}) +`, + }) concrete := func(a *NodeAbstractProvider) dag.Vertex { return &NodeApplyableProvider{ a, @@ -569,6 +571,53 @@ } } +func TestProviderFunctionTransformer_importBlock(t *testing.T) { + mod := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + required_providers { + aws = {} + } +} + +import { + to = aws_instance.foo + id = provider::aws::arn_build("test") +} + +resource "aws_instance" "foo" { +} +`, + }) + + var importTargets []*ImportTarget + for _, ic := range mod.Module.Import { + importTargets = append(importTargets, &ImportTarget{Config: ic}) + } + + g := testProviderTransformerGraph(t, mod, importTargets...) + + concrete := func(a *NodeAbstractProvider) dag.Vertex { + return &NodeApplyableProvider{NodeAbstractProvider: a} + } + + tf := testTransformProviders(concrete, mod) + if err := tf.Transform(t.Context(), g); err != nil { + t.Fatalf("err: %s", err) + } + + // The resource should have an edge to the provider, proving the + // provider function in the import block id was tracked. + expected := `aws_instance.foo + provider["registry.opentofu.org/hashicorp/aws"] +provider["registry.opentofu.org/hashicorp/aws"]` + + actual := strings.TrimSpace(g.String()) + if diff := cmp.Diff(actual, expected); diff != "" { + t.Fatalf("expected:\n%s", diff) + } +} + const testTransformProviderBasicStr = ` aws_instance.web provider["registry.opentofu.org/hashicorp/aws"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/version/VERSION new/opentofu-1.11.6/version/VERSION --- old/opentofu-1.11.5/version/VERSION 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/version/VERSION 2026-04-08 12:54:56.000000000 +0200 @@ -1 +1 @@ -1.11.5 +1.11.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/website/docs/internals/tracing.mdx new/opentofu-1.11.6/website/docs/internals/tracing.mdx --- old/opentofu-1.11.5/website/docs/internals/tracing.mdx 1970-01-01 01:00:00.000000000 +0100 +++ new/opentofu-1.11.6/website/docs/internals/tracing.mdx 2026-04-08 12:54:56.000000000 +0200 @@ -0,0 +1,122 @@ +--- +description: OpenTofu uses OpenTelemetry tracing to provide visibility into operations. All tracing is local-only and opt-in. +--- + +# OpenTelemetry Tracing + +:::warning Experimental Feature +OpenTelemetry tracing support is experimental and may change in future releases. +::: + +OpenTofu 1.10.0 introduces support for [OpenTelemetry](https://opentelemetry.io/) (OTel) tracing, providing visibility into OpenTofu's internal operations. This feature helps you debug performance issues, understand operation flows, and optimize your infrastructure workflows. + +## Privacy and Security + +OpenTofu's tracing implementation is designed with privacy as the top priority: + +- **Opt-in only**: Tracing is completely disabled by default +- **Local-only**: No telemetry data is sent to OpenTofu or any external service unless you explicitly configure it +- **Your infrastructure**: You control where traces are sent using standard OpenTelemetry configuration +- **Zero overhead**: When disabled, tracing has no performance impact + +As the OpenTofu team emphasizes: "Although this builds on a project named Open*Telemetry*, we are adding this support explicitly for you to trace *your application* using *your tooling* on *your infrastructure*." This feature is not intended to collect any data about OpenTofu usage patterns or user behavior for the OpenTofu project itself. + +## Quick Start + +### 1. Set up a tracing backend + +The easiest way to get started is with [Jaeger](https://www.jaegertracing.io/), an open-source distributed tracing platform: + +```bash +docker run -d --rm --name jaeger \ + -p 16686:16686 \ + -p 4317:4317 \ + -p 4318:4318 \ + jaegertracing/jaeger:latest +``` + +### 2. Configure OpenTofu + +Enable tracing by setting these environment variables: + +```bash +# Enable OTLP trace exporter +export OTEL_TRACES_EXPORTER=otlp + +# Point to your Jaeger instance +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 + +# Required for local development (skip TLS verification) +export OTEL_EXPORTER_OTLP_INSECURE=true +``` + +### 3. Run OpenTofu commands + +Now any OpenTofu command will generate traces: + +```bash +tofu init # Traces provider downloads +tofu plan # Traces planning operations +tofu apply # Traces apply workflow +``` + +### 4. View traces + +Open your browser and navigate to http://localhost:16686 to access the Jaeger UI. You'll see your OpenTofu operations broken down into detailed traces. + +## Configuration Options + +OpenTofu supports standard OpenTelemetry environment variables for configuration: + +### Basic Configuration + +| Variable | Description | Example | +|----------|-------------|---------| +| `OTEL_TRACES_EXPORTER` | Must be set to `otlp` to enable tracing | `otlp` | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint URL | `http://localhost:4317` | +| `OTEL_EXPORTER_OTLP_INSECURE` | Skip TLS verification | `true` for local dev | + +Additional OTLP configuration options are supported. See the [OpenTelemetry documentation](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options) for details. + +:::note +In this experimental implementation, OpenTofu always samples 100% of traces when tracing is enabled. Sampling configuration is not currently supported. +::: + +## Integration with Observability Platforms + +While Jaeger is great for getting started, OpenTofu's OpenTelemetry support works with any OTLP-compatible backend such as Grafana Tempo, AWS X-Ray, or Datadog. + +## Use Cases + +### Debugging Slow Operations + +Tracing helps identify bottlenecks in your OpenTofu workflows: +- Which provider downloads are slowest? +- How long does each resource take to plan/apply? +- Where is time being spent during initialization? + +### CI/CD Pipeline Optimization + +In continuous integration environments, tracing can reveal: +- Parallel operation conflicts +- Cache effectiveness +- Network latency issues +- Lock contention problems + +### Multi-Environment Deployments + +For complex deployments across regions or environments: +- Compare performance across different backends +- Identify region-specific delays +- Track provider version update impacts + +## Future Development + +This experimental feature is actively being developed. The OpenTofu team is particularly interested in feedback about: + +- Which additional operations should be traced +- Performance impact in real-world scenarios +- Integration experiences with different backends +- Specific use cases that benefit from tracing + +Please share your feedback through [GitHub issues](https://github.com/opentofu/opentofu/issues) or the [OpenTofu Slack community](https://opentofu.org/slack). \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/website/docs/language/modules/develop/index.mdx new/opentofu-1.11.6/website/docs/language/modules/develop/index.mdx --- old/opentofu-1.11.5/website/docs/language/modules/develop/index.mdx 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/website/docs/language/modules/develop/index.mdx 2026-04-08 12:54:56.000000000 +0200 @@ -74,3 +74,34 @@ OpenTofu uses that information during planning to reinterpret existing objects as if they had been created at the corresponding new addresses, eliminating a separate workflow step to replace or migrate existing objects. + +## Building blocks of a module +For a well-documented module that is ready to be published, there are some parts that the authors should take care of. + +### Readme +A root level README.md file should explain to users how the module should be used. +This file will show up in the Registry Search if the license allows for it. + +### License +The module should be licensed under one of +the [supported licenses](https://github.com/opentofu/registry-ui/blob/main/licenses.json). +If the module is not under one of these licenses, it will be findable in the [Registry Search](https://search.opentofu.org/), +but no other data will be displayed. + +### Submodules +A module can contain also submodules. To make the submodules show up in the Registry Search, +place the submodules in the `modules/MODULENAME` directory in the module directory. + +Each submodule directory can contain a `README.md` file to provide more information about the submodule scope. + +### Examples +Similar in structure to submodules, examples provide users with an easy way to get into using the module. +For an example to show up in the OpenTofu Registry Search, it needs to be placed into `examples/EXAMPLENAME` folder and +be accompanied by a `README.md` file providing more information about the example. + +### Testing the module +Tests are a great way to ensure that the module stays working when community pull requests come in. +The [`tofu test` command](../../../cli/commands/test/) has a host of tools that allow writing automated tests for +the module. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.11.5/website/docs/language/modules/develop/publish.mdx new/opentofu-1.11.6/website/docs/language/modules/develop/publish.mdx --- old/opentofu-1.11.5/website/docs/language/modules/develop/publish.mdx 2026-02-12 15:14:04.000000000 +0100 +++ new/opentofu-1.11.6/website/docs/language/modules/develop/publish.mdx 2026-04-08 12:54:56.000000000 +0200 @@ -24,7 +24,13 @@ instead use a [private registry](../../../internals/module-registry-protocol.mdx) to get the same benefits. -We welcome contributions of modules from our community members, partners, and customers. Our ecosystem is made richer by each new module created or an existing one updated, as they reflect the wide range of experience and technical requirements of the community that uses them. Our cloud provider partners often seek to develop specific modules for popular or challenging use cases on their platform and utilize them as valuable learning experiences to empathize with their users. Similarly, our community module developers incorporate a variety of opinions and use cases from the broader OpenTofu community. Both types of modules have their place in the registry, accessible to practitioners who can decide which modules best fit their requirements. +We welcome contributions of modules from our community members, partners, and customers. Our ecosystem is made +richer by each new module created or an existing one updated, as they reflect the wide range of experience and +technical requirements of the community that uses them. Our cloud provider partners often seek to develop specific +modules for popular or challenging use cases on their platform and utilize them as valuable learning experiences to +empathize with their users. Similarly, our community module developers incorporate a variety of opinions and use cases +from the broader OpenTofu community. Both types of modules have their place in the registry, accessible to +practitioners who can decide which modules best fit their requirements. ## Distribution via other sources @@ -38,3 +44,20 @@ [standard module structure](../../../language/modules/develop/structure.mdx) so that they can be used in a similar way as a registry module or be published on the registry at a later time. + +## OpenTofu registry module submission requirements +To be able to submit a module to the [OpenTofu registry](https://search.opentofu.org/), the following are the +requirements that need to be met: + +* The module must be hosted in a public GitHub repository. +* The module must be structured as described in [Building blocks of a module](./index.mdx#building-blocks-of-a-module). +* The module repository needs to follow the `terraform-<NAME>-<TARGETSYSTEM>` naming convention. + * `<NAME>` represents the platform on which the module acts. E.g.: `aws`. + * `<TARGETSYSTEM>` represents the type of infrastructure on the platform on which the module acts. This can be a multi-token string separated by hyphens (`-`). E.g.: `secret-manager`. + * Some examples of names: `terraform-aws-secret-manager`, `terraform-azurerm-avm-ptn-network-private-link-private-dns-zones`. +* Each version of the module should be a [`git tag`](https://git-scm.com/book/en/v2/Git-Basics-Tagging) whose name follows the [semantic versioning scheme](https://semver.org/). + +## Submitting the module to the OpenTofu registry +A module can be added to the OpenTofu registry by simply creating an issue by using the provided +[module template](https://github.com/opentofu/registry/issues/new?template=module.yml) and following the instructions +provided by it. ++++++ opentofu.obsinfo ++++++ --- /var/tmp/diff_new_pack.sBjL12/_old 2026-04-18 21:37:32.348449044 +0200 +++ /var/tmp/diff_new_pack.sBjL12/_new 2026-04-18 21:37:32.364449706 +0200 @@ -1,5 +1,5 @@ name: opentofu -version: 1.11.5 -mtime: 1770905644 -commit: 67fe9db49b7dafd46470cf9ac7f437aaa95f5c40 +version: 1.11.6 +mtime: 1775645696 +commit: 698e5e5204756963ffb28107fb61d77970e47f66 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/opentofu/vendor.tar.gz /work/SRC/openSUSE:Factory/.opentofu.new.11940/vendor.tar.gz differ: char 13, line 1
