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