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-06-13 18:48:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/opentofu (Old) and /work/SRC/openSUSE:Factory/.opentofu.new.1981 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "opentofu" Sat Jun 13 18:48:51 2026 rev:50 rq:1359089 version:1.12.2 Changes: -------- --- /work/SRC/openSUSE:Factory/opentofu/opentofu.changes 2026-05-29 18:10:20.090418031 +0200 +++ /work/SRC/openSUSE:Factory/.opentofu.new.1981/opentofu.changes 2026-06-13 18:51:03.234715046 +0200 @@ -1,0 +2,33 @@ +Sat Jun 13 10:04:32 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.12.2: + * SECURITY ADVISORIES: + - Previous releases in the v1.12 series could be affected by + several vulnerabilities: + - If for state encryption, OpenBao key provider is used with + wrapping algorithms, it could generate panics or hangs on + compromised systems where the JWE is specifically crafted. + This is fixed now by (#4177) + - Previous releases in the v1.12 series could be affected by + several vulnerabilities: + - When using SSH connections through OpenTofu, the errors + that were returned from attempting a connection could + include unescaped input bytes. + - If using an attacker-controlled server to run tofu against, + it might end up in high CPU consumption. + These are now fixed by (#4247) + * BUG FIXES: + - Properly handle EDEADLK during provider installation. On Unix + systems, the kernel may erroneously detect a deadlock between + tofu processes using the global plugin cache. (#4166) + - Fix race condition while handling closing signals during tofu + login, both when the signal is sent by the user and when the + browser fails to successfully connect. (4016) + - Prevent panic when using ephemeral resources during tofu + test`. (#4254) + * Dependencies + - [Govulncheck][v1.12] Upgrade go version 1.26.4 (#4247) + - [GO-2026-4945][main] Bump go-jose to the latest patched + version (#4203) + +------------------------------------------------------------------- Old: ---- opentofu-1.12.1.obscpio New: ---- opentofu-1.12.2.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ opentofu.spec ++++++ --- /var/tmp/diff_new_pack.khOCce/_old 2026-06-13 18:51:04.874785689 +0200 +++ /var/tmp/diff_new_pack.khOCce/_new 2026-06-13 18:51:04.878785861 +0200 @@ -19,7 +19,7 @@ %define executable_name tofu Name: opentofu -Version: 1.12.1 +Version: 1.12.2 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.26 >= 1.26.3 +BuildRequires: go1.26 >= 1.26.4 BuildRequires: golang-packaging # See: https://github.com/hashicorp/opentofu/issues/22807 ExcludeArch: %{ix86} %{arm} ++++++ _service ++++++ --- /var/tmp/diff_new_pack.khOCce/_old 2026-06-13 18:51:04.926787929 +0200 +++ /var/tmp/diff_new_pack.khOCce/_new 2026-06-13 18:51:04.926787929 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/opentofu/opentofu.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">refs/tags/v1.12.1</param> + <param name="revision">refs/tags/v1.12.2</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.khOCce/_old 2026-06-13 18:51:04.970789824 +0200 +++ /var/tmp/diff_new_pack.khOCce/_new 2026-06-13 18:51:04.974789997 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/opentofu/opentofu/</param> <param name="changesrevision">bdcbf091aab886499ed7718d7f558b428fffce39</param></service><service name="tar_scm"> <param name="url">https://github.com/opentofu/opentofu.git</param> - <param name="changesrevision">dc29882638869cdc60f871a112a04a415042caf1</param></service></servicedata> + <param name="changesrevision">75338c0c969e45d6aaa278b4071d3c64de837a9a</param></service></servicedata> (No newline at EOF) ++++++ opentofu-1.12.1.obscpio -> opentofu-1.12.2.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/CHANGELOG.md new/opentofu-1.12.2/CHANGELOG.md --- old/opentofu-1.12.1/CHANGELOG.md 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/CHANGELOG.md 2026-06-12 16:38:35.000000000 +0200 @@ -1,5 +1,28 @@ The v1.12.x release series is supported until **February 1 2027**. +## 1.12.3 (Unreleased) + +## 1.12.2 + +SECURITY ADVISORIES: + +* Previous releases in the v1.12 series could be affected by several vulnerabilities: + * If for state encryption, OpenBao key provider is used with wrapping algorithms, it could generate panics or hangs on compromised systems where the JWE is specifically crafted. + + This is fixed now by ([#4177](https://github.com/opentofu/opentofu/pull/4177)) + +* Previous releases in the v1.12 series could be affected by several vulnerabilities: + * When using SSH connections through OpenTofu, the errors that were returned from attempting a connection could include unescaped input bytes. + * If using an attacker-controlled server to run `tofu` against, it might end up in high CPU consumption. + + These are now fixed by ([#4247](https://github.com/opentofu/opentofu/pull/4247)) + +BUG FIXES: + +- Properly handle EDEADLK during provider installation. On Unix systems, the kernel may erroneously detect a deadlock between tofu processes using the global plugin cache. ([#4166](https://github.com/opentofu/opentofu/pull/4166)) +- Fix race condition while handling closing signals during `tofu login`, both when the signal is sent by the user and when the browser fails to successfully connect. ([4016](https://github.com/opentofu/opentofu/pull/4016)) +- Prevent panic when using ephemeral resources during tofu test`. ([#4254](https://github.com/opentofu/opentofu/pull/4253)) + ## 1.12.1 SECURITY ADVISORIES: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/go.mod new/opentofu-1.12.2/go.mod --- old/opentofu-1.12.1/go.mod 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/go.mod 2026-06-12 16:38:35.000000000 +0200 @@ -1,6 +1,6 @@ module github.com/opentofu/opentofu -go 1.26.3 +go 1.26.4 // 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. @@ -189,7 +189,7 @@ github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/go.sum new/opentofu-1.12.2/go.sum --- old/opentofu-1.12.1/go.sum 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/go.sum 2026-06-12 16:38:35.000000000 +0200 @@ -293,8 +293,8 @@ github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/e2etest/test_test.go new/opentofu-1.12.2/internal/command/e2etest/test_test.go --- old/opentofu-1.12.1/internal/command/e2etest/test_test.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/command/e2etest/test_test.go 2026-06-12 16:38:35.000000000 +0200 @@ -120,3 +120,35 @@ t.Errorf("output doesn't have expected success string:\n%s", stdout) } } + +func TestMockProviderWhenEphemeralInConfiguration(t *testing.T) { + // This test fetches providers from registry. + skipIfCannotAccessNetwork(t) + + tf := e2e.NewBinary(t, tofuBin, filepath.Join("testdata", "mock-test-with-ephemeral-in-configuration")) + + stdout, stderr, err := tf.Run("init") + if err != nil { + t.Errorf("unexpected error on 'init': %v", err) + } + if stderr != "" { + t.Errorf("unexpected stderr output on 'init':\n%s", stderr) + } + if stdout == "" { + t.Errorf("expected some output on 'init', got nothing") + } + + stdout, stderr, err = tf.Run("test") + if err != nil { + if strings.Contains(stdout, "OpenTofu crashed! This is always indicative of a bug within OpenTofu.") { + t.Errorf("Bug reproduced: running `tofu test` against a configuration that has an ephemeral resource.\n"+ + "This is the bug from https://github.com/opentofu/opentofu/issues/4251\n"+ + "stdout:\n%s", stdout) + return + } + t.Errorf("unexpected error on 'tofu test': %v\nstderr:\n%s\nstdout:\n%s", err, stderr, stdout) + } + if !strings.Contains(stdout, "1 passed, 0 failed") { + t.Errorf("output doesn't have the expected success string:\n%s", stdout) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tf new/opentofu-1.12.2/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tf --- old/opentofu-1.12.1/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tf 1970-01-01 01:00:00.000000000 +0100 +++ new/opentofu-1.12.2/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tf 2026-06-12 16:38:35.000000000 +0200 @@ -0,0 +1,30 @@ +# Minimal reproducer for https://github.com/opentofu/opentofu/issues/4251 +# Bug requires to run `tofu test` when the configuration contains also an `ephemeral` block + +terraform { + required_version = ">= 1.11.5" + required_providers { + random = { + source = "hashicorp/random" + version = "3.8.1" + } + sleep = { + source = "yottta/sleep" + version = "0.0.4" + } + } +} + +resource "random_id" "test" { + byte_length = 2 +} + +ephemeral "random_password" "test" { + length = 10 + +} + +resource "sleep_sleeper" "test" { + string_wo = ephemeral.random_password.test.result + string_wo_version = 1 +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tftest.hcl new/opentofu-1.12.2/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tftest.hcl --- old/opentofu-1.12.1/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tftest.hcl 1970-01-01 01:00:00.000000000 +0100 +++ new/opentofu-1.12.2/internal/command/e2etest/testdata/mock-test-with-ephemeral-in-configuration/main.tftest.hcl 2026-06-12 16:38:35.000000000 +0200 @@ -0,0 +1,10 @@ +mock_provider "random" {} + +run "happy_path" { + command = plan + assert { + condition = sleep_sleeper.test.string_wo == null + error_message = "Incorrect content for sleep_sleeper.test.string_wo" + + } +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/login.go new/opentofu-1.12.2/internal/command/login.go --- old/opentofu-1.12.1/internal/command/login.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/command/login.go 2026-06-12 16:38:35.000000000 +0200 @@ -19,6 +19,7 @@ "net/url" "path/filepath" "strings" + "sync" tfe "github.com/hashicorp/go-tfe" "github.com/mitchellh/cli" @@ -418,8 +419,14 @@ } // codeCh will allow our temporary HTTP server to transmit the OAuth code - // to the main execution path that follows. - codeCh := make(chan string) + // to the main execution path that follows. It is buffered so the handler + // can send without blocking even if the main goroutine has already moved on. + // Only the main goroutine may close codeCh, after all producers have stopped. + codeCh := make(chan string, 1) + // serverErrCh carries any unexpected error from server.Serve so that the + // main goroutine can handle it without a shared-state race on diags. + serverErrCh := make(chan error, 1) + var wg sync.WaitGroup server := &http.Server{ Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { log.Printf("[TRACE] login: request to callback server") @@ -442,12 +449,17 @@ return } - log.Printf("[TRACE] login: request contains an authorization code") - - // Send the code to our blocking wait below, so that the token - // fetching process can continue. - codeCh <- gotCode - close(codeCh) + // Non-blocking send: only the first callback request succeeds. + // Duplicate or concurrent requests are rejected with 400 so that + // the handler never blocks on the already-full buffered channel. + select { + case codeCh <- gotCode: + log.Printf("[TRACE] login: request contains an authorization code") + default: + log.Printf("[WARN] login: ignoring duplicate callback request") + resp.WriteHeader(400) + return + } log.Printf("[TRACE] login: returning response from callback server") @@ -460,21 +472,13 @@ }), } panicHandler := logging.PanicHandlerWithTraceFn() - go func() { + wg.Go(func() { defer panicHandler() err := server.Serve(listener) if err != nil && err != http.ErrServerClosed { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Can't start temporary login server", - fmt.Sprintf( - "The login process uses OAuth, which requires starting a temporary HTTP server on localhost. However, no TCP port numbers between %d and %d are available to create such a server.", - clientConfig.MinPort, clientConfig.MaxPort, - ), - )) - close(codeCh) + serverErrCh <- err } - }() + }) oauthConfig := &oauth2.Config{ ClientID: clientConfig.ID, @@ -509,7 +513,6 @@ view.WaitingForHostSignal() var code string - var ok bool select { case <-c.ShutdownCh: diags = diags.Append( @@ -519,22 +522,36 @@ "Current command was aborted by the calling code.", ), ) - code, ok = "", true - close(codeCh) - case code, ok = <-codeCh: + if err := server.Shutdown(ctx); err != nil { + log.Printf("[WARN] login: callback server shutdown failed: %s", err) } - - if !ok { - // If we got no code at all then the server wasn't able to start - // up, so we'll just give up. + wg.Wait() + close(codeCh) + return nil, diags + case serveErr := <-serverErrCh: + // The server failed to start up, so we'll just give up. + // No need to call Shutdown here: the server never started accepting + // requests, so there are no in-flight handler goroutines to wait for. + log.Printf("[ERROR] login: callback server error: %s", serveErr) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Can't start temporary login server", + fmt.Sprintf( + "The login process uses OAuth, which requires starting a temporary HTTP server on localhost. However, no TCP port numbers between %d and %d are available to create such a server.", + clientConfig.MinPort, clientConfig.MaxPort, + ), + )) + wg.Wait() + close(codeCh) return nil, diags + case code = <-codeCh: } - if err := server.Close(); err != nil { - // The server will close soon enough when our process exits anyway, - // so we won't fuss about it for right now. + if err := server.Shutdown(ctx); err != nil { log.Printf("[WARN] login: callback server can't shut down: %s", err) } + wg.Wait() + close(codeCh) if code == "" { // empty code is not possible in happy path as it is validated in the HTTP handler of our callback server diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/login_test.go new/opentofu-1.12.2/internal/command/login_test.go --- old/opentofu-1.12.1/internal/command/login_test.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/command/login_test.go 2026-06-12 16:38:35.000000000 +0200 @@ -11,6 +11,7 @@ "path/filepath" "strings" "testing" + "time" "github.com/opentofu/opentofu/internal/command/workdir" "github.com/opentofu/opentofu/internal/terminal" @@ -369,3 +370,161 @@ } }, false)) } + +// TestLoginOAuthCallbackRace verifies that no panic, deadlock, or data race +// occurs when ShutdownCh fires concurrently with the OAuth callback completing. +// +// Before the fix, interactiveGetTokenByCode had multiple goroutines calling +// close(codeCh), which could panic with "close of closed channel". The fix +// uses server.Shutdown() to wait for all in-flight handler goroutines to +// finish before closing codeCh, ensuring the main goroutine is the sole owner +// of the channel's lifetime. +// +// Run with the race detector to catch any remaining data races: +// +// go test -race -run TestLoginOAuthCallbackRace ./internal/command/ +func TestLoginOAuthCallbackRace(t *testing.T) { + s := httptest.NewServer(oauthserver.Handler) + defer s.Close() + + const iterations = 200 + + for i := range iterations { + func() { + defer func() { + if r := recover(); r != nil { + t.Errorf("iteration %d: panic in LoginCommand.Run (would crash tofu login): %v", i, r) + } + }() + + workDir := t.TempDir() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + loginView, done := testView(t) + defer done(t) + + creds := cliconfig.EmptyCredentialsSourceForTests( + filepath.Join(workDir, "credentials.tfrc.json"), + ) + svcs := disco.New( + disco.WithCredentials(creds), + disco.WithHTTPClient(httpclient.New(ctx)), + ) + svcs.ForceHostServices(svchost.Hostname("example.com"), map[string]any{ + "login.v1": map[string]any{ + "client": "anything-goes", + "authz": s.URL + "/authz", + "token": s.URL + "/token", + }, + }) + + abortCh := make(chan struct{}) + c := &LoginCommand{ + Meta: Meta{ + WorkingDir: workdir.NewDir("."), + View: loginView, + BrowserLauncher: webbrowser.NewMockLauncher(ctx), + Services: svcs, + ShutdownCh: abortCh, + }, + } + + defer testInputMap(t, map[string]string{ + "approve": "yes", + })() + + statusCh := make(chan int, 1) + go func() { + statusCh <- c.Run([]string{"example.com"}) + }() + + // Fire ShutdownCh at varying delays to hit different timing windows: + // - delay 0: ShutdownCh fires before MockLauncher visits the callback URL + // - small delay: races with the HTTP callback handler + // - larger delay: fires after OAuth is already complete + go func() { + time.Sleep(time.Duration(i%5) * time.Millisecond) + close(abortCh) + }() + + select { + case <-statusCh: + // exit status 0 (OAuth won) or 1 (ShutdownCh won) are both valid + case <-time.After(10 * time.Second): + t.Errorf("iteration %d: LoginCommand.Run did not return — possible deadlock", i) + } + }() + } +} + +// TestLoginOAuthCallbackNoPanicOnAbort verifies that sending to ShutdownCh +// before the OAuth callback arrives causes a clean abort without deadlock. +func TestLoginOAuthCallbackNoPanicOnAbort(t *testing.T) { + s := httptest.NewServer(oauthserver.Handler) + defer s.Close() + + for i := range 50 { + func() { + defer func() { + if r := recover(); r != nil { + t.Errorf("iteration %d: unexpected panic: %v", i, r) + } + }() + + workDir := t.TempDir() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + loginView, done := testView(t) + defer done(t) + + creds := cliconfig.EmptyCredentialsSourceForTests( + filepath.Join(workDir, "credentials.tfrc.json"), + ) + svcs := disco.New( + disco.WithCredentials(creds), + disco.WithHTTPClient(httpclient.New(ctx)), + ) + svcs.ForceHostServices(svchost.Hostname("example.com"), map[string]any{ + "login.v1": map[string]any{ + "client": "anything-goes", + "authz": s.URL + "/authz", + "token": s.URL + "/token", + }, + }) + + // No MockLauncher: the OAuth callback will never arrive, so ShutdownCh + // is the only way to unblock the command. + abortCh := make(chan struct{}) + c := &LoginCommand{ + Meta: Meta{ + WorkingDir: workdir.NewDir("."), + View: loginView, + Services: svcs, + ShutdownCh: abortCh, + }, + } + + defer testInputMap(t, map[string]string{ + "approve": "yes", + })() + + statusCh := make(chan int, 1) + go func() { + statusCh <- c.Run([]string{"example.com"}) + }() + + close(abortCh) + + select { + case status := <-statusCh: + if status != 1 { + t.Errorf("iteration %d: expected exit status 1 after abort, got %d", i, status) + } + case <-time.After(10 * time.Second): + t.Errorf("iteration %d: LoginCommand.Run did not return after ShutdownCh — deadlock", i) + } + }() + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/command/views/state_test.go new/opentofu-1.12.2/internal/command/views/state_test.go --- old/opentofu-1.12.1/internal/command/views/state_test.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/command/views/state_test.go 2026-06-12 16:38:35.000000000 +0200 @@ -19,6 +19,7 @@ "github.com/opentofu/opentofu/internal/states/statefile" "github.com/opentofu/opentofu/internal/tfdiags" "github.com/opentofu/opentofu/internal/tofu" + "github.com/opentofu/opentofu/version" regaddr "github.com/opentofu/registry-address/v2" "github.com/zclconf/go-cty/cty" ) @@ -532,7 +533,7 @@ wantJson: []map[string]any{ { "format_version": "1.0", - "terraform_version": "1.12.0", + "terraform_version": version.SemVer.String(), "values": map[string]any{ "root_module": map[string]any{ "resources": []any{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/flock/filesystem_lock_unix.go new/opentofu-1.12.2/internal/flock/filesystem_lock_unix.go --- old/opentofu-1.12.1/internal/flock/filesystem_lock_unix.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/flock/filesystem_lock_unix.go 2026-06-12 16:38:35.000000000 +0200 @@ -11,6 +11,7 @@ "context" "fmt" "io" + "log" "os" "syscall" ) @@ -34,6 +35,22 @@ // If the given context is cancelled then it returns early with the cancellation // error. func LockBlocking(ctx context.Context, f *os.File) error { + err := lockBlockingInner(ctx, f) + + // On some platforms (MacOS, Linux, ...?), fcntl tries to detect deadlocks. Unfortunately with multiple + // processes and multiple goroutines, we may trip the detector erroniously. It thinks that + // because different processes are installing different providers and have a mix of goroutines + // (sleeping trying to lock) / (active locked), there must be a deadlock. + // + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html + for err == syscall.EDEADLK { + log.Printf("[INFO] Locking deadlock incorrectly detected by the kernel on %s", f.Name()) + err = lockBlockingInner(ctx, f) + } + + return err +} +func lockBlockingInner(ctx context.Context, f *os.File) error { flock := &syscall.Flock_t{ Type: syscall.F_RDLCK | syscall.F_WRLCK, Whence: int16(io.SeekStart), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/internal/tofu/provider_for_test_framework.go new/opentofu-1.12.2/internal/tofu/provider_for_test_framework.go --- old/opentofu-1.12.1/internal/tofu/provider_for_test_framework.go 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/internal/tofu/provider_for_test_framework.go 2026-06-12 16:38:35.000000000 +0200 @@ -116,19 +116,28 @@ return resp } -func (p providerForTest) OpenEphemeralResource(_ context.Context, _ providers.OpenEphemeralResourceRequest) (resp providers.OpenEphemeralResourceResponse) { - // TODO ephemeral testing support - implement me when adding testing support - panic("implement me") +func (p providerForTest) OpenEphemeralResource(_ context.Context, r providers.OpenEphemeralResourceRequest) (resp providers.OpenEphemeralResourceResponse) { + resSchema, _ := p.schema.SchemaForResourceType(addrs.EphemeralResourceMode, r.TypeName) + + resp.Result, resp.Diagnostics = newMockValueComposer(r.TypeName).ComposeBySchema(resSchema.Block, r.Config, p.overrideValues) + return resp } func (p providerForTest) RenewEphemeralResource(_ context.Context, _ providers.RenewEphemeralResourceRequest) (resp providers.RenewEphemeralResourceResponse) { // TODO ephemeral testing support - implement me when adding testing support + + // In order to fix the issue reported in https://github.com/opentofu/opentofu/issues/4251, OpenEphemeralResource and CloseEphemeralResource + // had their `panic` call removed and implemented properly to ensure that `tofu test` can be executed against a + // configuration containing `ephemeral` blocks. The fix provided just fixed the panic without implementing any + // testing functionality for ephemeral resources. Therefore, RenewEphemeralResource has no reason to be implemented + // because it cannot be reached as it relies on OpenEphemeralResource to return a specific value in the RenewAt to + // have this called. Without any testing functionality to mock the value for RenewAt, this will never be called so + // we want to have the panic in place. panic("implement me") } func (p providerForTest) CloseEphemeralResource(_ context.Context, _ providers.CloseEphemeralResourceRequest) (resp providers.CloseEphemeralResourceResponse) { - // TODO ephemeral testing support - implement me when adding testing support - panic("implement me") + return resp } // ValidateProviderConfig is irrelevant when provider is mocked or overridden. @@ -149,7 +158,6 @@ return providerSchema } - // providerForTest doesn't configure its internal provider because it is mocked. func (p providerForTest) ConfigureProvider(context.Context, providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { return providers.ConfigureProviderResponse{} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/version/VERSION new/opentofu-1.12.2/version/VERSION --- old/opentofu-1.12.1/version/VERSION 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/version/VERSION 2026-06-12 16:38:35.000000000 +0200 @@ -1 +1 @@ -1.12.1 +1.12.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/website/docs/internals/tracing.mdx new/opentofu-1.12.2/website/docs/internals/tracing.mdx --- old/opentofu-1.12.1/website/docs/internals/tracing.mdx 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/website/docs/internals/tracing.mdx 2026-06-12 16:38:35.000000000 +0200 @@ -44,7 +44,7 @@ export OTEL_TRACES_EXPORTER=otlp # Point to your Jaeger instance -export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # Required for local development (skip TLS verification) export OTEL_EXPORTER_OTLP_INSECURE=true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.1/website/docs/language/import/index.mdx new/opentofu-1.12.2/website/docs/language/import/index.mdx --- old/opentofu-1.12.1/website/docs/language/import/index.mdx 2026-05-27 10:51:46.000000000 +0200 +++ new/opentofu-1.12.2/website/docs/language/import/index.mdx 2026-06-12 16:38:35.000000000 +0200 @@ -9,11 +9,11 @@ While we do not expect to make backwards-incompatible changes to syntax, the `-generate-config-out` flag and how OpenTofu processes imports during the plan stage and generates configuration may change in future releases. ::: -Use the `import` block to import existing infrastructure resources into OpenTofu, bringing them under OpenTofu's management. Unlike the `tofu import` command, configuration-driven import using `import` blocks is predictable, works with CICD pipelines, and lets you preview an import operation before modifying state. +Use the `import` block to import existing infrastructure objects into OpenTofu, bringing them under OpenTofu's management. Unlike the `tofu import` command, configuration-driven import using `import` blocks is predictable, works with CICD pipelines, and lets you preview an import operation before modifying state. -Once imported, OpenTofu tracks the resource in your state file. You can then manage the imported resource like any other, updating its attributes and destroying it as part of a standard resource lifecycle. +Once imported, OpenTofu tracks the object as a resource instance in your state file. You can then manage the imported object like any other, updating its attributes and destroying it as part of a standard resource lifecycle. -The `import` block records that OpenTofu imported the resource and did not create it. After importing, you can optionally remove import blocks from your configuration or leave them as a record of the resource's origin. +The `import` block represents that OpenTofu imported the object and did not create it. After importing, you can optionally remove import blocks from your configuration or leave them as a record of the object's origin. ## Syntax @@ -21,8 +21,10 @@ ```hcl import { + identity = { + id = "i-abcd1234" + } to = aws_instance.example - id = "i-abcd1234" } resource "aws_instance" "example" { @@ -34,44 +36,62 @@ The above `import` block defines an import of the AWS instance with the ID "i-abcd1234" into the `aws_instance.example` resource in the root module. The `import` block has the following arguments: -- `to` - The instance address this resource will have in your state file. -- `id` - A string with the [import ID](#import-id) of the resource. -- `provider` (optional) - An optional custom resource provider, see [The Resource provider Meta-Argument](../../language/meta-arguments/resource-provider.mdx) for details. -- `for_each` (optional) - Import several resources by iterating over a map or a set. See [Importing multiple resources](#importing-multiple-resources) below. +- Either `identity` or `id` - To [specify which remote object to import](#import-id). +- `to` - The resource instance address that OpenTofu will use to track this object in [OpenTofu state](../state/index.mdx). +- `provider` (optional) - The provider instance to use for importing, as with [The Resource provider Meta-Argument](../../language/meta-arguments/resource-provider.mdx). +- `for_each` (optional) - Import several objects as multiple instances of the same resource by iterating over a map or a set. See [Importing multiple resources](#importing-multiple-resources) below. If you do not set the `provider` argument, OpenTofu attempts to import from the default provider. -### Import ID +### Specifying what to import {#import-id} -The import block requires you to provide the `id` argument with a literal string of your resource's import ID. OpenTofu needs this import ID to locate the resource you want to import. +The `identity` and `id` arguments offer two different ways to specify which remote object you intend to import. These arguments are mutually-exclusive. -The identifier you use for a resource's import ID is resource-specific. You can find the required ID in the provider's documentation for the resource you wish to import. +The `identity` argument is the modern approach which specifies the remote object by specifying one or more attributes whose names and types vary depending on the resource type's _identity schema_. For example, importing into an `aws_instance` resource instance requires an object with a single `id` attribute: -## Plan and apply an import +```hcl +import { + identity = { + id = "i-abcd1234" + } + to = aws_instance.example +} +``` -OpenTofu processes the `import` block during the plan stage. Once a plan is approved, OpenTofu imports the resource into its state during the subsequent apply stage. +Not all resource types have been updated to support `identity` yet. For older resource types you must use the top-level `id` argument instead and specify the remote object using a single string in whatever form the provider is expecting. For example, `aws_instance` expects the instance ID directly when using this legacy approach: + +```hcl +import { + id = "i-abcd1234" + to = aws_instance.example +} +``` + +Refer to the documentation for the resource type you are importing into to learn whether that resource type has an identity schema to use with `identity`, or if it still requires you to use the legacy `id` argument. + +## Plan and apply an import -To import a resource using `import` blocks, you must: -1. Define an `import` block for the resource(s). -1. Add a corresponding `resource` block to your configuration , or [generate configuration](../../language/import/generating-configuration.mdx) for that resource. -1. Run `tofu plan` to review how OpenTofu will import the resource(s). -1. Apply the configuration to import the resources and update your OpenTofu state. +OpenTofu processes the `import` block during the plan stage. Once a plan is approved, OpenTofu imports the object into its state during the subsequent apply stage. -The `import` block is [_idempotent_](https://en.wikipedia.org/wiki/Idempotence), meaning that applying an import action and running another plan will not generate another import action as long as that resource remains in your state. +To import an object using `import` blocks, you must: +1. Write `import` blocks describing the relationship between the remote objects and the resource instance addresses OpenTofu should use to track them. +1. Add the corresponding `resource` blocks to your configuration , or [generate initial configuration automatically](../../language/import/generating-configuration.mdx). +1. Run `tofu plan` to review how OpenTofu will import the objects. +1. Apply the plan to import the objects and update your OpenTofu state. -OpenTofu only needs to import a given resource once. Attempting to import a resource into the same address again is a harmless no-op. You can remove `import` blocks after completing the import or safely leave them in your configuration as a record of the resource's origin for future module maintainers. For more information on maintaining configurations over time, see [Refactoring](../../language/modules/develop/refactoring.mdx). +An `import` block is active only if OpenTofu is not already tracking an object with the address given in `to`. After importing is successful, an `import` block becomes inert and you can optionally remove it from your configuration or retain it as a historical record for future maintainers. For more information on maintaining configurations over time, refer to [Refactoring](../../language/modules/develop/refactoring.mdx) ## Resource configuration -Before importing, you must add configuration for every resource you want OpenTofu to import. Otherwise, OpenTofu throws an error during planning, insisting you add resource configuration before it can successfully import. You can create resource configuration manually or [generate it using OpenTofu](../../language/import/generating-configuration.mdx). +Before importing, you must add a `resource` block for every object (or collection of related objects) you want OpenTofu to import. Otherwise, OpenTofu raises an error during planning, insisting you add resource configuration before it can successfully import. You can create resource configuration manually or [generate it using OpenTofu](../../language/import/generating-configuration.mdx). We recommend writing a `resource` block if you know what most of the [resource's arguments](../../language/resources/syntax.mdx#resource-arguments) will be. For example, your configuration may already contain a similar resource whose configuration you can copy and modify. -We recommend [generating configuration](../../language/import/generating-configuration.mdx) when importing multiple resources or a single complex resource that you do not already have the configuration for. +We recommend [generating configuration](../../language/import/generating-configuration.mdx) when importing multiple objects or a single complex object that you do not already have the configuration for. ### Add a `resource` block -Add a `resource` block for the resource to import. The resource address must match the import block's `to` argument. +Add a `resource` block for the resource to import into. The `to` argument in the `import` block must refer to that resource. ```hcl import { @@ -86,8 +106,8 @@ ### Generate configuration -OpenTofu can generate HCL for resources that do not already exist in configuration. -For more details, see [Generating Configuration](../../language/import/generating-configuration.mdx). +OpenTofu can generate `resource` blocks for resources that are not already declared in your configuration. +For more details, refer to [Generating Configuration](../../language/import/generating-configuration.mdx). ## Examples @@ -133,9 +153,9 @@ } ``` -### Importing multiple resources +### Importing multiple objects {#importing-multiple-resources} -You can import multiple resources with one import block by using a `for_each` expression. This expression accepts [a set, a tuple or a map](../../language/expressions/types.mdx) and provides the `each.key` and `each.value` variables to access the individual elements. +You can import multiple resources instances with one import block by using a `for_each` expression. This expression accepts [a set, a tuple or a map](../../language/expressions/types.mdx) and provides the `each.key` and `each.value` variables to access the individual elements. In the example below, you can specify a list of server IDs to be imported. If you specify an empty list, the `random_id` resource will generate all IDs randomly. If you specify some IDs, the import block will import the specified IDs and the resource will randomly generate the rest. Note, the `random_id` resource requires the IDs to be in base64 format. ++++++ opentofu.obsinfo ++++++ --- /var/tmp/diff_new_pack.khOCce/_old 2026-06-13 18:51:10.219015881 +0200 +++ /var/tmp/diff_new_pack.khOCce/_new 2026-06-13 18:51:10.223016054 +0200 @@ -1,5 +1,5 @@ name: opentofu -version: 1.12.1 -mtime: 1779871906 -commit: dc29882638869cdc60f871a112a04a415042caf1 +version: 1.12.2 +mtime: 1781275115 +commit: 75338c0c969e45d6aaa278b4071d3c64de837a9a ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/opentofu/vendor.tar.gz /work/SRC/openSUSE:Factory/.opentofu.new.1981/vendor.tar.gz differ: char 116, line 2
