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

Reply via email to