This is an automated email from the ASF dual-hosted git repository.
kaxilnaik pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/master by this push:
new 9e3b2c5 GCP Secrets Optional Lookup (#12360)
9e3b2c5 is described below
commit 9e3b2c554dadf58972198e4e16f15af2f15ec37a
Author: Faisal <[email protected]>
AuthorDate: Thu Nov 19 14:27:04 2020 -0500
GCP Secrets Optional Lookup (#12360)
---
.../google/cloud/secrets/secret_manager.py | 23 +++++++++---
.../aws-secrets-manaager-backend.rst | 15 ++++++++
.../aws-ssm-parameter-store-secrets-backend.rst | 16 ++++++++
.../azure-key-vault-secrets-backend.rst | 15 ++++++++
.../google-cloud-secret-manager-backend.rst | 16 ++++++++
.../hashicorp-vault-secrets-backend.rst | 15 ++++++++
.../google/cloud/secrets/test_secret_manager.py | 43 ++++++++++++++++++++++
.../azure/secrets/test_azure_key_vault.py | 6 +--
8 files changed, 141 insertions(+), 8 deletions(-)
diff --git a/airflow/providers/google/cloud/secrets/secret_manager.py
b/airflow/providers/google/cloud/secrets/secret_manager.py
index 0322d86..f647223 100644
--- a/airflow/providers/google/cloud/secrets/secret_manager.py
+++ b/airflow/providers/google/cloud/secrets/secret_manager.py
@@ -52,11 +52,14 @@ class CloudSecretManagerBackend(BaseSecretsBackend,
LoggingMixin):
The full secret id should follow the pattern "[a-zA-Z0-9-_]".
:param connections_prefix: Specifies the prefix of the secret to read to
get Connections.
+ If set to None (null), requests for connections will not be sent to
GCP Secrets Manager
:type connections_prefix: str
:param variables_prefix: Specifies the prefix of the secret to read to get
Variables.
+ If set to None (null), requests for variables will not be sent to GCP
Secrets Manager
:type variables_prefix: str
:param config_prefix: Specifies the prefix of the secret to read to get
Airflow Configurations
containing secrets.
+ If set to None (null), requests for configurations will not be sent to
GCP Secrets Manager
:type config_prefix: str
:param gcp_key_path: Path to Google Cloud Service Account key file (JSON).
Mutually exclusive with
gcp_keyfile_dict. use default credentials in the current environment
if not provided.
@@ -89,11 +92,12 @@ class CloudSecretManagerBackend(BaseSecretsBackend,
LoggingMixin):
self.variables_prefix = variables_prefix
self.config_prefix = config_prefix
self.sep = sep
- if not self._is_valid_prefix_and_sep():
- raise AirflowException(
- "`connections_prefix`, `variables_prefix` and `sep` should "
- f"follows that pattern {SECRET_ID_PATTERN}"
- )
+ if connections_prefix is not None:
+ if not self._is_valid_prefix_and_sep():
+ raise AirflowException(
+ "`connections_prefix`, `variables_prefix` and `sep` should
"
+ f"follows that pattern {SECRET_ID_PATTERN}"
+ )
self.credentials, self.project_id = get_credentials_and_project_id(
keyfile_dict=gcp_keyfile_dict, key_path=gcp_key_path,
scopes=gcp_scopes
)
@@ -121,6 +125,9 @@ class CloudSecretManagerBackend(BaseSecretsBackend,
LoggingMixin):
:param conn_id: connection id
:type conn_id: str
"""
+ if self.connections_prefix is None:
+ return None
+
return self._get_secret(self.connections_prefix, conn_id)
def get_variable(self, key: str) -> Optional[str]:
@@ -130,6 +137,9 @@ class CloudSecretManagerBackend(BaseSecretsBackend,
LoggingMixin):
:param key: Variable Key
:return: Variable Value
"""
+ if self.variables_prefix is None:
+ return None
+
return self._get_secret(self.variables_prefix, key)
def get_config(self, key: str) -> Optional[str]:
@@ -139,6 +149,9 @@ class CloudSecretManagerBackend(BaseSecretsBackend,
LoggingMixin):
:param key: Configuration Option Key
:return: Configuration Option Value
"""
+ if self.config_prefix is None:
+ return None
+
return self._get_secret(self.config_prefix, key)
def _get_secret(self, path_prefix: str, secret_id: str) -> Optional[str]:
diff --git
a/docs/security/secrets/secrets-backend/aws-secrets-manaager-backend.rst
b/docs/security/secrets/secrets-backend/aws-secrets-manaager-backend.rst
index d52c5e4..53da605 100644
--- a/docs/security/secrets/secrets-backend/aws-secrets-manaager-backend.rst
+++ b/docs/security/secrets/secrets-backend/aws-secrets-manaager-backend.rst
@@ -32,6 +32,21 @@ Here is a sample configuration:
To authenticate you can either supply a profile name to reference aws profile,
e.g. defined in ``~/.aws/config`` or set
environment variables like ``AWS_ACCESS_KEY_ID``, ``AWS_SECRET_ACCESS_KEY``.
+Optional lookup
+"""""""""""""""
+
+Optionally connections, variables, or config may be looked up exclusive of
each other or in any combination.
+This will prevent requests being sent to AWS Secrets Manager for the excluded
type.
+
+If you want to look up some and not others in AWS Secrets Manager you may do
so by setting the relevant ``*_prefix`` parameter of the ones to be excluded as
``null``.
+
+For example, if you want to set parameter ``connections_prefix`` to
``"airflow/connections"`` and not look up variables, your configuration file
should look like this:
+
+.. code-block:: ini
+
+ [secrets]
+ backend =
airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend
+ backend_kwargs = {"connections_prefix": "airflow/connections",
"variables_prefix": null, "profile_name": "default"}
Storing and Retrieving Connections
""""""""""""""""""""""""""""""""""
diff --git
a/docs/security/secrets/secrets-backend/aws-ssm-parameter-store-secrets-backend.rst
b/docs/security/secrets/secrets-backend/aws-ssm-parameter-store-secrets-backend.rst
index 4d99800..c1d7072 100644
---
a/docs/security/secrets/secrets-backend/aws-ssm-parameter-store-secrets-backend.rst
+++
b/docs/security/secrets/secrets-backend/aws-ssm-parameter-store-secrets-backend.rst
@@ -31,6 +31,22 @@ Here is a sample configuration:
backend =
airflow.providers.amazon.aws.secrets.systems_manager.SystemsManagerParameterStoreBackend
backend_kwargs = {"connections_prefix": "/airflow/connections",
"variables_prefix": "/airflow/variables", "profile_name": "default"}
+Optional lookup
+"""""""""""""""
+
+Optionally connections, variables, or config may be looked up exclusive of
each other or in any combination.
+This will prevent requests being sent to AWS SSM Parameter Store for the
excluded type.
+
+If you want to look up some and not others in AWS SSM Parameter Store you may
do so by setting the relevant ``*_prefix`` parameter of the ones to be excluded
as ``null``.
+
+For example, if you want to set parameter ``connections_prefix`` to
``"/airflow/connections"`` and not look up variables, your configuration file
should look like this:
+
+.. code-block:: ini
+
+ [secrets]
+ backend =
airflow.providers.amazon.aws.secrets.systems_manager.SystemsManagerParameterStoreBackend
+ backend_kwargs = {"connections_prefix": "/airflow/connections",
"variables_prefix": null, "profile_name": "default"}
+
Storing and Retrieving Connections
""""""""""""""""""""""""""""""""""
diff --git
a/docs/security/secrets/secrets-backend/azure-key-vault-secrets-backend.rst
b/docs/security/secrets/secrets-backend/azure-key-vault-secrets-backend.rst
index 9c704f1..d214f81 100644
--- a/docs/security/secrets/secrets-backend/azure-key-vault-secrets-backend.rst
+++ b/docs/security/secrets/secrets-backend/azure-key-vault-secrets-backend.rst
@@ -34,6 +34,21 @@ Here is a sample configuration:
For client authentication, the ``DefaultAzureCredential`` from the Azure
Python SDK is used as credential provider,
which supports service principal, managed identity and user credentials.
+Optional lookup
+"""""""""""""""
+
+Optionally connections, variables, or config may be looked up exclusive of
each other or in any combination.
+This will prevent requests being sent to Azure Key Vault for the excluded type.
+
+If you want to look up some and not others in Azure Key Vault you may do so by
setting the relevant ``*_prefix`` parameter of the ones to be excluded as
``null``.
+
+For example, if you want to set parameter ``connections_prefix`` to
``"airflow-connections"`` and not look up variables, your configuration file
should look like this:
+
+.. code-block:: ini
+
+ [secrets]
+ backend =
airflow.providers.microsoft.azure.secrets.azure_key_vault.AzureKeyVaultBackend
+ backend_kwargs = {"connections_prefix": "airflow-connections",
"variables_prefix": null, "vault_url":
"https://example-akv-resource-name.vault.azure.net/"}
Storing and Retrieving Connections
""""""""""""""""""""""""""""""""""
diff --git
a/docs/security/secrets/secrets-backend/google-cloud-secret-manager-backend.rst
b/docs/security/secrets/secrets-backend/google-cloud-secret-manager-backend.rst
index b405c74..6fee203 100644
---
a/docs/security/secrets/secrets-backend/google-cloud-secret-manager-backend.rst
+++
b/docs/security/secrets/secrets-backend/google-cloud-secret-manager-backend.rst
@@ -87,6 +87,22 @@ For example, if you want to set parameter
``connections_prefix`` to ``"airflow-t
backend =
airflow.providers.google.cloud.secrets.secret_manager.CloudSecretManagerBackend
backend_kwargs = {"connections_prefix": "airflow-tenant-primary",
"variables_prefix": "airflow-tenant-primary"}
+Optional lookup
+"""""""""""""""
+
+Optionally connections, variables, or config may be looked up exclusive of
each other or in any combination.
+This will prevent requests being sent to GCP Secrets Manager for the excluded
type.
+
+If you want to look up some and not others in GCP Secrets Manager you may do
so by setting the relevant ``*_prefix`` parameter of the ones to be excluded as
``null``.
+
+For example, if you want to set parameter ``connections_prefix`` to
``"airflow-tenant-primary"`` and not look up variables, your configuration file
should look like this:
+
+.. code-block:: ini
+
+ [secrets]
+ backend =
airflow.providers.google.cloud.secrets.secret_manager.CloudSecretManagerBackend
+ backend_kwargs = {"connections_prefix": "airflow-tenant-primary",
"variables_prefix": null}
+
Set-up credentials
""""""""""""""""""
diff --git
a/docs/security/secrets/secrets-backend/hashicorp-vault-secrets-backend.rst
b/docs/security/secrets/secrets-backend/hashicorp-vault-secrets-backend.rst
index 1b25060..09d3576 100644
--- a/docs/security/secrets/secrets-backend/hashicorp-vault-secrets-backend.rst
+++ b/docs/security/secrets/secrets-backend/hashicorp-vault-secrets-backend.rst
@@ -44,6 +44,21 @@ key to ``backend_kwargs``:
export VAULT_ADDR="http://127.0.0.1:8200"
+Optional lookup
+"""""""""""""""
+
+Optionally connections, variables, or config may be looked up exclusive of
each other or in any combination.
+This will prevent requests being sent to Vault for the excluded type.
+
+If you want to look up some and not others in Vault you may do so by setting
the relevant ``*_path`` parameter of the ones to be excluded as ``null``.
+
+For example, if you want to set parameter ``connections_path`` to
``"airflow-connections"`` and not look up variables, your configuration file
should look like this:
+
+.. code-block:: ini
+
+ [secrets]
+ backend = airflow.providers.hashicorp.secrets.vault.VaultBackend
+ backend_kwargs = {"connections_path": "airflow-connections",
"variables_path": null, "mount_point": "airflow", "url":
"http://127.0.0.1:8200"}
Storing and Retrieving Connections
""""""""""""""""""""""""""""""""""
diff --git a/tests/providers/google/cloud/secrets/test_secret_manager.py
b/tests/providers/google/cloud/secrets/test_secret_manager.py
index a11fc3e..2b2cbf0 100644
--- a/tests/providers/google/cloud/secrets/test_secret_manager.py
+++ b/tests/providers/google/cloud/secrets/test_secret_manager.py
@@ -206,3 +206,46 @@ class TestCloudSecretManagerBackend(TestCase):
log_output.output[0],
f"Google Cloud API Call Error \\(NotFound\\): Secret ID
{secret_id} not found",
)
+
+ @mock.patch(MODULE_NAME + ".get_credentials_and_project_id")
+ @mock.patch(CLIENT_MODULE_NAME + ".SecretManagerServiceClient")
+ def test_connections_prefix_none_value(self, mock_client_callable,
mock_get_creds):
+ mock_get_creds.return_value = CREDENTIALS, PROJECT_ID
+ mock_client = mock.MagicMock()
+ mock_client_callable.return_value = mock_client
+
+ with mock.patch(MODULE_NAME +
'.CloudSecretManagerBackend._get_secret') as mock_get_secret:
+ with mock.patch(
+ MODULE_NAME +
'.CloudSecretManagerBackend._is_valid_prefix_and_sep'
+ ) as mock_is_valid_prefix_sep:
+ secrets_manager_backend =
CloudSecretManagerBackend(connections_prefix=None)
+
+ mock_is_valid_prefix_sep.assert_not_called()
+
self.assertIsNone(secrets_manager_backend.get_conn_uri(conn_id=CONN_ID))
+ mock_get_secret.assert_not_called()
+
+ @mock.patch(MODULE_NAME + ".get_credentials_and_project_id")
+ @mock.patch(CLIENT_MODULE_NAME + ".SecretManagerServiceClient")
+ def test_variables_prefix_none_value(self, mock_client_callable,
mock_get_creds):
+ mock_get_creds.return_value = CREDENTIALS, PROJECT_ID
+ mock_client = mock.MagicMock()
+ mock_client_callable.return_value = mock_client
+
+ with mock.patch(MODULE_NAME +
'.CloudSecretManagerBackend._get_secret') as mock_get_secret:
+ secrets_manager_backend =
CloudSecretManagerBackend(variables_prefix=None)
+
+ self.assertIsNone(secrets_manager_backend.get_variable(VAR_KEY))
+ mock_get_secret.assert_not_called()
+
+ @mock.patch(MODULE_NAME + ".get_credentials_and_project_id")
+ @mock.patch(CLIENT_MODULE_NAME + ".SecretManagerServiceClient")
+ def test_config_prefix_none_value(self, mock_client_callable,
mock_get_creds):
+ mock_get_creds.return_value = CREDENTIALS, PROJECT_ID
+ mock_client = mock.MagicMock()
+ mock_client_callable.return_value = mock_client
+
+ with mock.patch(MODULE_NAME +
'.CloudSecretManagerBackend._get_secret') as mock_get_secret:
+ secrets_manager_backend =
CloudSecretManagerBackend(config_prefix=None)
+
+ self.assertIsNone(secrets_manager_backend.get_config(CONFIG_KEY))
+ mock_get_secret.assert_not_called()
diff --git a/tests/providers/microsoft/azure/secrets/test_azure_key_vault.py
b/tests/providers/microsoft/azure/secrets/test_azure_key_vault.py
index ec2cc78..e2cef7a 100644
--- a/tests/providers/microsoft/azure/secrets/test_azure_key_vault.py
+++ b/tests/providers/microsoft/azure/secrets/test_azure_key_vault.py
@@ -114,7 +114,7 @@ class TestAzureKeyVaultBackend(TestCase):
backend = AzureKeyVaultBackend(**kwargs)
self.assertIsNone(backend.get_conn_uri('test_mysql'))
- mock_get_secret._get_secret.assert_not_called()
+ mock_get_secret.assert_not_called()
@mock.patch('airflow.providers.microsoft.azure.secrets.azure_key_vault.AzureKeyVaultBackend._get_secret')
def test_variable_prefix_none_value(self, mock_get_secret):
@@ -127,7 +127,7 @@ class TestAzureKeyVaultBackend(TestCase):
backend = AzureKeyVaultBackend(**kwargs)
self.assertIsNone(backend.get_variable('hello'))
- mock_get_secret._get_secret.assert_not_called()
+ mock_get_secret.assert_not_called()
@mock.patch('airflow.providers.microsoft.azure.secrets.azure_key_vault.AzureKeyVaultBackend._get_secret')
def test_config_prefix_none_value(self, mock_get_secret):
@@ -140,4 +140,4 @@ class TestAzureKeyVaultBackend(TestCase):
backend = AzureKeyVaultBackend(**kwargs)
self.assertIsNone(backend.get_config('test_mysql'))
- mock_get_secret._get_secret.assert_not_called()
+ mock_get_secret.assert_not_called()