This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 8900f8b2caa Fix CloudSecretManagerBackend regression with explicit 
project_id (#61654)
8900f8b2caa is described below

commit 8900f8b2caa0d16664855924c7542e8e4dafc425
Author: Darshan Gorasiya <[email protected]>
AuthorDate: Tue May 12 22:56:57 2026 +0100

    Fix CloudSecretManagerBackend regression with explicit project_id (#61654)
    
    * Fix CloudSecretManagerBackend regression with explicit project_id (issue 
#61217)
    
    Previously,  would fail when
    Application Default Credentials (ADC) had no default project, because
     raised an AirflowException before the explicit
    project_id could be applied.
    
    Changes:
    1.  returns empty string ("") instead of raising
       when ADC yields None project_id.
    2.  now validates that a project_id is
       available (either from ADC or explicit parameter) and raises a clear 
error.
    
    Backward compatibility:
    - Callers that relied on the exception still get one (now from the backend).
    - Callers that pass an explicit project_id now work correctly.
    - No change to the public API of .
    
    * Apply black and isort formatting
    
    * Add missing unit tests for CloudSecretManagerBackend regression fix
    
    - Add test for get_credentials_and_project_id returning empty string when 
ADC yields None project_id
    - Add validation tests for CloudSecretManagerBackend project_id 
determination
    - Addresses review feedback about missing tests and AI disclosure
    
    Fixes: #61217
    
    * Fix static checks: ruff import order and whitespace
    
    * Replace AirflowException with ValueError for missing project_id validation
    
    * Update known airflowexceptions
    
    ---------
    
    Co-authored-by: Darshankumar Gorasiya <[email protected]>
    Co-authored-by: Shahar Epstein <[email protected]>
---
 .../providers/google/cloud/secrets/secret_manager.py |  7 +++++++
 .../google/cloud/utils/credentials_provider.py       |  5 +----
 .../unit/google/cloud/secrets/test_secret_manager.py | 20 ++++++++++++++++++++
 .../google/cloud/utils/test_credentials_provider.py  |  9 +++++++++
 scripts/ci/prek/known_airflow_exceptions.txt         |  2 +-
 5 files changed, 38 insertions(+), 5 deletions(-)

diff --git 
a/providers/google/src/airflow/providers/google/cloud/secrets/secret_manager.py 
b/providers/google/src/airflow/providers/google/cloud/secrets/secret_manager.py
index ba6898d51e2..2b505499124 100644
--- 
a/providers/google/src/airflow/providers/google/cloud/secrets/secret_manager.py
+++ 
b/providers/google/src/airflow/providers/google/cloud/secrets/secret_manager.py
@@ -136,6 +136,13 @@ class CloudSecretManagerBackend(BaseSecretsBackend, 
LoggingMixin):
         if project_id:
             self.project_id = project_id
 
+        if not self.project_id:
+            raise ValueError(
+                "Project ID could not be determined. "
+                "Please provide 'project_id' in backend configuration or 
ensure "
+                "your credentials include a default project."
+            )
+
     @property
     def client(self) -> _SecretManagerClient:
         """
diff --git 
a/providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py
 
b/providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py
index 3c53c04195e..b363d233e6e 100644
--- 
a/providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py
+++ 
b/providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py
@@ -418,10 +418,7 @@ class _CredentialProvider(LoggingMixin):
         scopes = list(self.scopes) if self.scopes else None
         credentials, project_id = google.auth.default(scopes=scopes)
         if not project_id:
-            raise AirflowException(
-                "Project ID could not be determined from default credentials. "
-                "Please provide `key_secret_project_id` parameter."
-            )
+            project_id = ""
         return credentials, project_id
 
     def _log_info(self, *args, **kwargs) -> None:
diff --git 
a/providers/google/tests/unit/google/cloud/secrets/test_secret_manager.py 
b/providers/google/tests/unit/google/cloud/secrets/test_secret_manager.py
index 76f277049d1..7d2d381cf7c 100644
--- a/providers/google/tests/unit/google/cloud/secrets/test_secret_manager.py
+++ b/providers/google/tests/unit/google/cloud/secrets/test_secret_manager.py
@@ -252,3 +252,23 @@ class TestCloudSecretManagerBackend:
 
             assert secrets_manager_backend.get_config(CONFIG_KEY) is None
             mock_get_secret.assert_not_called()
+
+    @mock.patch(MODULE_NAME + ".get_credentials_and_project_id")
+    @mock.patch(CLIENT_MODULE_NAME + ".SecretManagerServiceClient")
+    def test_init_raises_when_project_id_not_determined(self, 
mock_client_callable, mock_get_creds):
+        mock_get_creds.return_value = CREDENTIALS, ""
+        mock_client = mock.MagicMock()
+        mock_client_callable.return_value = mock_client
+
+        with pytest.raises(ValueError, match="Project ID could not be 
determined"):
+            CloudSecretManagerBackend()
+
+    @mock.patch(MODULE_NAME + ".get_credentials_and_project_id")
+    @mock.patch(CLIENT_MODULE_NAME + ".SecretManagerServiceClient")
+    def test_init_succeeds_with_explicit_project_id(self, 
mock_client_callable, mock_get_creds):
+        mock_get_creds.return_value = CREDENTIALS, ""
+        mock_client = mock.MagicMock()
+        mock_client_callable.return_value = mock_client
+
+        backend = CloudSecretManagerBackend(project_id="explicit-project")
+        assert backend.project_id == "explicit-project"
diff --git 
a/providers/google/tests/unit/google/cloud/utils/test_credentials_provider.py 
b/providers/google/tests/unit/google/cloud/utils/test_credentials_provider.py
index ac68884f06b..872f8780d92 100644
--- 
a/providers/google/tests/unit/google/cloud/utils/test_credentials_provider.py
+++ 
b/providers/google/tests/unit/google/cloud/utils/test_credentials_provider.py
@@ -517,6 +517,15 @@ class TestGetGcpCredentialsAndProjectId:
                 client_secret=CLIENT_SECRET,
             )
 
+    @mock.patch("google.auth.default")
+    def 
test_get_credentials_and_project_id_with_default_auth_no_project_id(self, 
mock_auth_default):
+        mock_credentials = mock.MagicMock()
+        mock_auth_default.return_value = (mock_credentials, None)
+
+        result = get_credentials_and_project_id()
+        mock_auth_default.assert_called_once_with(scopes=None)
+        assert result == (mock_credentials, "")
+
 
 class TestGetScopes:
     def test_get_scopes_with_default(self):
diff --git a/scripts/ci/prek/known_airflow_exceptions.txt 
b/scripts/ci/prek/known_airflow_exceptions.txt
index e56e96c35a3..4d700bcb81d 100644
--- a/scripts/ci/prek/known_airflow_exceptions.txt
+++ b/scripts/ci/prek/known_airflow_exceptions.txt
@@ -314,7 +314,7 @@ 
providers/google/src/airflow/providers/google/cloud/triggers/cloud_composer.py::
 providers/google/src/airflow/providers/google/cloud/triggers/cloud_run.py::1
 providers/google/src/airflow/providers/google/cloud/triggers/dataproc.py::5
 
providers/google/src/airflow/providers/google/cloud/triggers/kubernetes_engine.py::1
-providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py::17
+providers/google/src/airflow/providers/google/cloud/utils/credentials_provider.py::16
 providers/google/src/airflow/providers/google/common/hooks/base_google.py::7
 
providers/google/src/airflow/providers/google/common/hooks/operation_helpers.py::2
 providers/google/src/airflow/providers/google/firebase/hooks/firestore.py::1

Reply via email to