apache-airflow-providers-hashicorp 4.3.0rc1__tar.gz → 4.3.1rc1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of apache-airflow-providers-hashicorp might be problematic. Click here for more details.
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/PKG-INFO +8 -9
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/README.rst +4 -4
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/changelog.rst +17 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/index.rst +3 -3
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/provider.yaml +2 -1
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/pyproject.toml +4 -5
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/__init__.py +1 -1
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/_internal_client/vault_client.py +40 -3
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/hooks/vault.py +5 -1
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/_internal_client/test_vault_client.py +161 -28
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/hooks/test_vault.py +70 -19
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/.latest-doc-only-change.txt +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/commits.rst +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/conf.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/connections/vault.rst +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/installing-providers-from-sources.rst +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/integration-logos/Hashicorp-Vault.png +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/redirects.txt +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/secrets-backends/hashicorp-vault.rst +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/docs/security.rst +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/LICENSE +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/_internal_client/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/get_provider_info.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/hooks/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/secrets/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/src/airflow/providers/hashicorp/secrets/vault.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/conftest.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/_internal_client/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/hooks/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/secrets/__init__.py +0 -0
- {apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/tests/unit/hashicorp/secrets/test_vault.py +0 -0
{apache_airflow_providers_hashicorp-4.3.0rc1 → apache_airflow_providers_hashicorp-4.3.1rc1}/PKG-INFO
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apache-airflow-providers-hashicorp
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.1rc1
|
|
4
4
|
Summary: Provider package apache-airflow-providers-hashicorp for Apache Airflow
|
|
5
5
|
Keywords: airflow-provider,hashicorp,airflow,integration
|
|
6
6
|
Author-email: Apache Software Foundation <dev@airflow.apache.org>
|
|
7
7
|
Maintainer-email: Apache Software Foundation <dev@airflow.apache.org>
|
|
8
|
-
Requires-Python: ~=3.
|
|
8
|
+
Requires-Python: ~=3.10
|
|
9
9
|
Description-Content-Type: text/x-rst
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
11
|
Classifier: Environment :: Console
|
|
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: System Administrators
|
|
|
15
15
|
Classifier: Framework :: Apache Airflow
|
|
16
16
|
Classifier: Framework :: Apache Airflow :: Provider
|
|
17
17
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -25,8 +24,8 @@ Requires-Dist: hvac>=1.1.0
|
|
|
25
24
|
Requires-Dist: boto3>=1.33.0 ; extra == "boto3"
|
|
26
25
|
Requires-Dist: apache-airflow-providers-google ; extra == "google"
|
|
27
26
|
Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
|
|
28
|
-
Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
29
|
-
Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
27
|
+
Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/changelog.html
|
|
28
|
+
Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1
|
|
30
29
|
Project-URL: Mastodon, https://fosstodon.org/@airflow
|
|
31
30
|
Project-URL: Slack Chat, https://s.apache.org/airflow-slack
|
|
32
31
|
Project-URL: Source Code, https://github.com/apache/airflow
|
|
@@ -59,7 +58,7 @@ Provides-Extra: google
|
|
|
59
58
|
|
|
60
59
|
Package ``apache-airflow-providers-hashicorp``
|
|
61
60
|
|
|
62
|
-
Release: ``4.3.
|
|
61
|
+
Release: ``4.3.1``
|
|
63
62
|
|
|
64
63
|
|
|
65
64
|
Hashicorp including `Hashicorp Vault <https://www.vaultproject.io/>`__
|
|
@@ -72,7 +71,7 @@ This is a provider package for ``hashicorp`` provider. All classes for this prov
|
|
|
72
71
|
are in ``airflow.providers.hashicorp`` python package.
|
|
73
72
|
|
|
74
73
|
You can find package information and changelog for the provider
|
|
75
|
-
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
74
|
+
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/>`_.
|
|
76
75
|
|
|
77
76
|
Installation
|
|
78
77
|
------------
|
|
@@ -81,7 +80,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
|
|
|
81
80
|
for the minimum Airflow version supported) via
|
|
82
81
|
``pip install apache-airflow-providers-hashicorp``
|
|
83
82
|
|
|
84
|
-
The package supports the following python versions: 3.
|
|
83
|
+
The package supports the following python versions: 3.10,3.11,3.12
|
|
85
84
|
|
|
86
85
|
Requirements
|
|
87
86
|
------------
|
|
@@ -113,5 +112,5 @@ Dependent package
|
|
|
113
112
|
==================================================================================================== ==========
|
|
114
113
|
|
|
115
114
|
The changelog for the provider package can be found in the
|
|
116
|
-
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
115
|
+
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/changelog.html>`_.
|
|
117
116
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
Package ``apache-airflow-providers-hashicorp``
|
|
25
25
|
|
|
26
|
-
Release: ``4.3.
|
|
26
|
+
Release: ``4.3.1``
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
Hashicorp including `Hashicorp Vault <https://www.vaultproject.io/>`__
|
|
@@ -36,7 +36,7 @@ This is a provider package for ``hashicorp`` provider. All classes for this prov
|
|
|
36
36
|
are in ``airflow.providers.hashicorp`` python package.
|
|
37
37
|
|
|
38
38
|
You can find package information and changelog for the provider
|
|
39
|
-
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
39
|
+
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/>`_.
|
|
40
40
|
|
|
41
41
|
Installation
|
|
42
42
|
------------
|
|
@@ -45,7 +45,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
|
|
|
45
45
|
for the minimum Airflow version supported) via
|
|
46
46
|
``pip install apache-airflow-providers-hashicorp``
|
|
47
47
|
|
|
48
|
-
The package supports the following python versions: 3.
|
|
48
|
+
The package supports the following python versions: 3.10,3.11,3.12
|
|
49
49
|
|
|
50
50
|
Requirements
|
|
51
51
|
------------
|
|
@@ -77,4 +77,4 @@ Dependent package
|
|
|
77
77
|
==================================================================================================== ==========
|
|
78
78
|
|
|
79
79
|
The changelog for the provider package can be found in the
|
|
80
|
-
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
80
|
+
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/changelog.html>`_.
|
|
@@ -27,6 +27,23 @@
|
|
|
27
27
|
Changelog
|
|
28
28
|
---------
|
|
29
29
|
|
|
30
|
+
4.3.1
|
|
31
|
+
.....
|
|
32
|
+
|
|
33
|
+
Bug Fixes
|
|
34
|
+
~~~~~~~~~
|
|
35
|
+
|
|
36
|
+
* ``Fix gcp auth in hashicorp vault provider. (#51991)``
|
|
37
|
+
|
|
38
|
+
Misc
|
|
39
|
+
~~~~
|
|
40
|
+
|
|
41
|
+
* ``Move 'BaseHook' implementation to task SDK (#51873)``
|
|
42
|
+
* ``Drop support for Python 3.9 (#52072)``
|
|
43
|
+
|
|
44
|
+
.. Below changes are excluded from the changelog. Move them to
|
|
45
|
+
appropriate section above if needed. Do not delete the lines(!):
|
|
46
|
+
|
|
30
47
|
4.3.0
|
|
31
48
|
.....
|
|
32
49
|
|
|
@@ -69,7 +69,7 @@ apache-airflow-providers-hashicorp package
|
|
|
69
69
|
Hashicorp including `Hashicorp Vault <https://www.vaultproject.io/>`__
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
Release: 4.3.
|
|
72
|
+
Release: 4.3.1
|
|
73
73
|
|
|
74
74
|
Provider package
|
|
75
75
|
----------------
|
|
@@ -121,5 +121,5 @@ Downloading official packages
|
|
|
121
121
|
You can download officially released packages and verify their checksums and signatures from the
|
|
122
122
|
`Official Apache Download site <https://downloads.apache.org/airflow/providers/>`_
|
|
123
123
|
|
|
124
|
-
* `The apache-airflow-providers-hashicorp 4.3.
|
|
125
|
-
* `The apache-airflow-providers-hashicorp 4.3.
|
|
124
|
+
* `The apache-airflow-providers-hashicorp 4.3.1 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1.tar.gz.sha512>`__)
|
|
125
|
+
* `The apache-airflow-providers-hashicorp 4.3.1 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_hashicorp-4.3.1-py3-none-any.whl.sha512>`__)
|
|
@@ -22,12 +22,13 @@ description: |
|
|
|
22
22
|
Hashicorp including `Hashicorp Vault <https://www.vaultproject.io/>`__
|
|
23
23
|
|
|
24
24
|
state: ready
|
|
25
|
-
source-date-epoch:
|
|
25
|
+
source-date-epoch: 1751473545
|
|
26
26
|
# Note that those versions are maintained by release manager - do not update them manually
|
|
27
27
|
# with the exception of case where other provider in sources has >= new provider version.
|
|
28
28
|
# In such case adding >= NEW_VERSION and bumping to NEW_VERSION in a provider have
|
|
29
29
|
# to be done in the same PR
|
|
30
30
|
versions:
|
|
31
|
+
- 4.3.1
|
|
31
32
|
- 4.3.0
|
|
32
33
|
- 4.2.0
|
|
33
34
|
- 4.1.1
|
|
@@ -25,7 +25,7 @@ build-backend = "flit_core.buildapi"
|
|
|
25
25
|
|
|
26
26
|
[project]
|
|
27
27
|
name = "apache-airflow-providers-hashicorp"
|
|
28
|
-
version = "4.3.
|
|
28
|
+
version = "4.3.1rc1"
|
|
29
29
|
description = "Provider package apache-airflow-providers-hashicorp for Apache Airflow"
|
|
30
30
|
readme = "README.rst"
|
|
31
31
|
authors = [
|
|
@@ -44,13 +44,12 @@ classifiers = [
|
|
|
44
44
|
"Framework :: Apache Airflow",
|
|
45
45
|
"Framework :: Apache Airflow :: Provider",
|
|
46
46
|
"License :: OSI Approved :: Apache Software License",
|
|
47
|
-
"Programming Language :: Python :: 3.9",
|
|
48
47
|
"Programming Language :: Python :: 3.10",
|
|
49
48
|
"Programming Language :: Python :: 3.11",
|
|
50
49
|
"Programming Language :: Python :: 3.12",
|
|
51
50
|
"Topic :: System :: Monitoring",
|
|
52
51
|
]
|
|
53
|
-
requires-python = "~=3.
|
|
52
|
+
requires-python = "~=3.10"
|
|
54
53
|
|
|
55
54
|
# The dependencies should be modified in place in the generated file.
|
|
56
55
|
# Any change in the dependencies is preserved when the file is regenerated
|
|
@@ -107,8 +106,8 @@ apache-airflow-providers-common-sql = {workspace = true}
|
|
|
107
106
|
apache-airflow-providers-standard = {workspace = true}
|
|
108
107
|
|
|
109
108
|
[project.urls]
|
|
110
|
-
"Documentation" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
111
|
-
"Changelog" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.
|
|
109
|
+
"Documentation" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1"
|
|
110
|
+
"Changelog" = "https://airflow.staged.apache.org/docs/apache-airflow-providers-hashicorp/4.3.1/changelog.html"
|
|
112
111
|
"Bug Tracker" = "https://github.com/apache/airflow/issues"
|
|
113
112
|
"Source Code" = "https://github.com/apache/airflow"
|
|
114
113
|
"Slack Chat" = "https://s.apache.org/airflow-slack"
|
|
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
|
|
|
29
29
|
|
|
30
30
|
__all__ = ["__version__"]
|
|
31
31
|
|
|
32
|
-
__version__ = "4.3.
|
|
32
|
+
__version__ = "4.3.1"
|
|
33
33
|
|
|
34
34
|
if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
|
|
35
35
|
"2.10.0"
|
|
@@ -153,6 +153,15 @@ class _VaultClient(LoggingMixin):
|
|
|
153
153
|
raise VaultError("The 'radius' authentication type requires 'radius_host'")
|
|
154
154
|
if not radius_secret:
|
|
155
155
|
raise VaultError("The 'radius' authentication type requires 'radius_secret'")
|
|
156
|
+
if auth_type == "gcp":
|
|
157
|
+
if not gcp_scopes:
|
|
158
|
+
raise VaultError("The 'gcp' authentication type requires 'gcp_scopes'")
|
|
159
|
+
if not role_id:
|
|
160
|
+
raise VaultError("The 'gcp' authentication type requires 'role_id'")
|
|
161
|
+
if not gcp_key_path and not gcp_keyfile_dict:
|
|
162
|
+
raise VaultError(
|
|
163
|
+
"The 'gcp' authentication type requires 'gcp_key_path' or 'gcp_keyfile_dict'"
|
|
164
|
+
)
|
|
156
165
|
|
|
157
166
|
self.kv_engine_version = kv_engine_version or 2
|
|
158
167
|
self.url = url
|
|
@@ -303,13 +312,41 @@ class _VaultClient(LoggingMixin):
|
|
|
303
312
|
)
|
|
304
313
|
|
|
305
314
|
scopes = _get_scopes(self.gcp_scopes)
|
|
306
|
-
credentials,
|
|
315
|
+
credentials, project_id = get_credentials_and_project_id(
|
|
307
316
|
key_path=self.gcp_key_path, keyfile_dict=self.gcp_keyfile_dict, scopes=scopes
|
|
308
317
|
)
|
|
318
|
+
|
|
319
|
+
import json
|
|
320
|
+
import time
|
|
321
|
+
|
|
322
|
+
import googleapiclient
|
|
323
|
+
|
|
324
|
+
if self.gcp_keyfile_dict:
|
|
325
|
+
creds = self.gcp_keyfile_dict
|
|
326
|
+
elif self.gcp_key_path:
|
|
327
|
+
with open(self.gcp_key_path) as f:
|
|
328
|
+
creds = json.load(f)
|
|
329
|
+
|
|
330
|
+
service_account = creds["client_email"]
|
|
331
|
+
|
|
332
|
+
# Generate a payload for subsequent "signJwt()" call
|
|
333
|
+
# Reference: https://googleapis.dev/python/google-auth/latest/reference/google.auth.jwt.html#google.auth.jwt.Credentials
|
|
334
|
+
now = int(time.time())
|
|
335
|
+
expires = now + 900 # 15 mins in seconds, can't be longer.
|
|
336
|
+
payload = {"iat": now, "exp": expires, "sub": credentials, "aud": f"vault/{self.role_id}"}
|
|
337
|
+
body = {"payload": json.dumps(payload)}
|
|
338
|
+
name = f"projects/{project_id}/serviceAccounts/{service_account}"
|
|
339
|
+
|
|
340
|
+
# Perform the GCP API call
|
|
341
|
+
iam = googleapiclient.discovery.build("iam", "v1", credentials=credentials)
|
|
342
|
+
request = iam.projects().serviceAccounts().signJwt(name=name, body=body)
|
|
343
|
+
resp = request.execute()
|
|
344
|
+
jwt = resp["signedJwt"]
|
|
345
|
+
|
|
309
346
|
if self.auth_mount_point:
|
|
310
|
-
_client.auth.gcp.
|
|
347
|
+
_client.auth.gcp.login(role=self.role_id, jwt=jwt, mount_point=self.auth_mount_point)
|
|
311
348
|
else:
|
|
312
|
-
_client.auth.gcp.
|
|
349
|
+
_client.auth.gcp.login(role=self.role_id, jwt=jwt)
|
|
313
350
|
|
|
314
351
|
def _auth_azure(self, _client: hvac.Client) -> None:
|
|
315
352
|
if self.auth_mount_point:
|
|
@@ -23,12 +23,16 @@ from typing import TYPE_CHECKING, Any
|
|
|
23
23
|
|
|
24
24
|
from hvac.exceptions import VaultError
|
|
25
25
|
|
|
26
|
-
from airflow.hooks.base import BaseHook
|
|
27
26
|
from airflow.providers.hashicorp._internal_client.vault_client import (
|
|
28
27
|
DEFAULT_KUBERNETES_JWT_PATH,
|
|
29
28
|
DEFAULT_KV_ENGINE_VERSION,
|
|
30
29
|
_VaultClient,
|
|
31
30
|
)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from airflow.sdk import BaseHook
|
|
34
|
+
except ImportError:
|
|
35
|
+
from airflow.hooks.base import BaseHook # type: ignore[attr-defined,no-redef]
|
|
32
36
|
from airflow.utils.helpers import merge_dicts
|
|
33
37
|
|
|
34
38
|
if TYPE_CHECKING:
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
# under the License.
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
import json
|
|
20
|
+
import time
|
|
19
21
|
from unittest import mock
|
|
20
22
|
from unittest.mock import mock_open, patch
|
|
21
23
|
|
|
@@ -253,86 +255,217 @@ class TestVaultClient:
|
|
|
253
255
|
secret_id="pass",
|
|
254
256
|
)
|
|
255
257
|
|
|
258
|
+
@mock.patch("builtins.open", create=True)
|
|
256
259
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider._get_scopes")
|
|
257
260
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
258
|
-
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
259
|
-
|
|
261
|
+
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac.Client")
|
|
262
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
263
|
+
def test_gcp(self, mock_google_build, mock_hvac_client, mock_get_credentials, mock_get_scopes, mock_open):
|
|
264
|
+
# Mock the content of the file 'path.json'
|
|
265
|
+
mock_file = mock.MagicMock()
|
|
266
|
+
mock_file.read.return_value = '{"client_email": "service_account_email"}'
|
|
267
|
+
mock_open.return_value.__enter__.return_value = mock_file
|
|
268
|
+
|
|
260
269
|
mock_client = mock.MagicMock()
|
|
261
|
-
|
|
270
|
+
mock_hvac_client.return_value = mock_client
|
|
262
271
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
263
272
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
273
|
+
|
|
274
|
+
# Mock the current time to use for iat and exp
|
|
275
|
+
current_time = int(time.time())
|
|
276
|
+
iat = current_time
|
|
277
|
+
exp = iat + 3600 # 1 hour after iat
|
|
278
|
+
|
|
279
|
+
# Mock the signJwt API to return the expected payload
|
|
280
|
+
mock_sign_jwt = (
|
|
281
|
+
mock_google_build.return_value.projects.return_value.serviceAccounts.return_value.signJwt
|
|
282
|
+
)
|
|
283
|
+
mock_sign_jwt.return_value.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
284
|
+
|
|
264
285
|
vault_client = _VaultClient(
|
|
265
286
|
auth_type="gcp",
|
|
266
287
|
gcp_key_path="path.json",
|
|
267
288
|
gcp_scopes="scope1,scope2",
|
|
289
|
+
role_id="role",
|
|
268
290
|
url="http://localhost:8180",
|
|
269
291
|
session=None,
|
|
270
292
|
)
|
|
271
|
-
|
|
272
|
-
|
|
293
|
+
|
|
294
|
+
# Preserve the original json.dumps
|
|
295
|
+
original_json_dumps = json.dumps
|
|
296
|
+
|
|
297
|
+
# Inject the mocked payload into the JWT signing process
|
|
298
|
+
with mock.patch("json.dumps") as mock_json_dumps:
|
|
299
|
+
|
|
300
|
+
def mocked_json_dumps(payload):
|
|
301
|
+
# Override the payload to inject controlled iat and exp values
|
|
302
|
+
payload["iat"] = iat
|
|
303
|
+
payload["exp"] = exp
|
|
304
|
+
return original_json_dumps(payload) # Use the original json.dumps
|
|
305
|
+
|
|
306
|
+
mock_json_dumps.side_effect = mocked_json_dumps
|
|
307
|
+
|
|
308
|
+
client = vault_client.client # Trigger the Vault client creation
|
|
309
|
+
|
|
310
|
+
# Validate that the HVAC client and other mocks are called correctly
|
|
311
|
+
mock_hvac_client.assert_called_with(url="http://localhost:8180", session=None)
|
|
273
312
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
274
313
|
mock_get_credentials.assert_called_with(
|
|
275
314
|
key_path="path.json", keyfile_dict=None, scopes=["scope1", "scope2"]
|
|
276
315
|
)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
)
|
|
316
|
+
|
|
317
|
+
# Extract the arguments passed to the mocked signJwt API
|
|
318
|
+
args, kwargs = mock_sign_jwt.call_args
|
|
319
|
+
payload = json.loads(kwargs["body"]["payload"])
|
|
320
|
+
|
|
321
|
+
# Assert iat and exp values are as expected
|
|
322
|
+
assert payload["iat"] == iat
|
|
323
|
+
assert payload["exp"] == exp
|
|
324
|
+
assert abs(payload["exp"] - (payload["iat"] + 3600)) < 10 # Validate exp is 3600 seconds after iat
|
|
325
|
+
|
|
326
|
+
client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt")
|
|
281
327
|
client.is_authenticated.assert_called_with()
|
|
282
328
|
assert vault_client.kv_engine_version == 2
|
|
283
329
|
|
|
330
|
+
@mock.patch("builtins.open", create=True)
|
|
284
331
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider._get_scopes")
|
|
285
332
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
286
|
-
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
333
|
+
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac.Client")
|
|
334
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
335
|
+
def test_gcp_different_auth_mount_point(
|
|
336
|
+
self, mock_google_build, mock_hvac_client, mock_get_credentials, mock_get_scopes, mock_open
|
|
337
|
+
):
|
|
338
|
+
# Mock the content of the file 'path.json'
|
|
339
|
+
mock_file = mock.MagicMock()
|
|
340
|
+
mock_file.read.return_value = '{"client_email": "service_account_email"}'
|
|
341
|
+
mock_open.return_value.__enter__.return_value = mock_file
|
|
342
|
+
|
|
343
|
+
mock_client = mock.MagicMock()
|
|
344
|
+
mock_hvac_client.return_value = mock_client
|
|
290
345
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
291
346
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
347
|
+
|
|
348
|
+
mock_sign_jwt = (
|
|
349
|
+
mock_google_build.return_value.projects.return_value.serviceAccounts.return_value.signJwt
|
|
350
|
+
)
|
|
351
|
+
mock_sign_jwt.return_value.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
352
|
+
|
|
353
|
+
# Generate realistic iat and exp values
|
|
354
|
+
current_time = int(time.time())
|
|
355
|
+
iat = current_time
|
|
356
|
+
exp = current_time + 3600 # 1 hour later
|
|
357
|
+
|
|
292
358
|
vault_client = _VaultClient(
|
|
293
359
|
auth_type="gcp",
|
|
294
360
|
gcp_key_path="path.json",
|
|
295
361
|
gcp_scopes="scope1,scope2",
|
|
362
|
+
role_id="role",
|
|
296
363
|
url="http://localhost:8180",
|
|
297
364
|
auth_mount_point="other",
|
|
298
365
|
session=None,
|
|
299
366
|
)
|
|
300
|
-
|
|
301
|
-
|
|
367
|
+
|
|
368
|
+
# Preserve the original json.dumps
|
|
369
|
+
original_json_dumps = json.dumps
|
|
370
|
+
|
|
371
|
+
# Inject the mocked payload into the JWT signing process
|
|
372
|
+
with mock.patch("json.dumps") as mock_json_dumps:
|
|
373
|
+
|
|
374
|
+
def mocked_json_dumps(payload):
|
|
375
|
+
# Override the payload to inject controlled iat and exp values
|
|
376
|
+
payload["iat"] = iat
|
|
377
|
+
payload["exp"] = exp
|
|
378
|
+
return original_json_dumps(payload) # Use the original json.dumps
|
|
379
|
+
|
|
380
|
+
mock_json_dumps.side_effect = mocked_json_dumps
|
|
381
|
+
|
|
382
|
+
client = vault_client.client # Trigger the Vault client creation
|
|
383
|
+
|
|
384
|
+
# Assertions
|
|
385
|
+
mock_hvac_client.assert_called_with(url="http://localhost:8180", session=None)
|
|
302
386
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
303
387
|
mock_get_credentials.assert_called_with(
|
|
304
388
|
key_path="path.json", keyfile_dict=None, scopes=["scope1", "scope2"]
|
|
305
389
|
)
|
|
306
|
-
|
|
307
|
-
|
|
390
|
+
# Extract the arguments passed to the mocked signJwt API
|
|
391
|
+
args, kwargs = mock_sign_jwt.call_args
|
|
392
|
+
payload = json.loads(kwargs["body"]["payload"])
|
|
393
|
+
|
|
394
|
+
# Assert iat and exp values are as expected
|
|
395
|
+
assert payload["iat"] == iat
|
|
396
|
+
assert payload["exp"] == exp
|
|
397
|
+
assert abs(payload["exp"] - (payload["iat"] + 3600)) < 10 # Validate exp is 3600 seconds after iat
|
|
398
|
+
|
|
399
|
+
client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt", mount_point="other")
|
|
308
400
|
client.is_authenticated.assert_called_with()
|
|
309
401
|
assert vault_client.kv_engine_version == 2
|
|
310
402
|
|
|
403
|
+
@mock.patch(
|
|
404
|
+
"builtins.open", new_callable=mock_open, read_data='{"client_email": "service_account_email"}'
|
|
405
|
+
)
|
|
311
406
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider._get_scopes")
|
|
312
407
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
313
|
-
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
314
|
-
|
|
408
|
+
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac.Client")
|
|
409
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
410
|
+
def test_gcp_dict(
|
|
411
|
+
self, mock_google_build, mock_hvac_client, mock_get_credentials, mock_get_scopes, mock_file
|
|
412
|
+
):
|
|
315
413
|
mock_client = mock.MagicMock()
|
|
316
|
-
|
|
414
|
+
mock_hvac_client.return_value = mock_client
|
|
317
415
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
318
416
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
417
|
+
|
|
418
|
+
mock_sign_jwt = (
|
|
419
|
+
mock_google_build.return_value.projects.return_value.serviceAccounts.return_value.signJwt
|
|
420
|
+
)
|
|
421
|
+
mock_sign_jwt.return_value.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
422
|
+
|
|
423
|
+
# Generate realistic iat and exp values
|
|
424
|
+
current_time = int(time.time())
|
|
425
|
+
iat = current_time
|
|
426
|
+
exp = current_time + 3600 # 1 hour later
|
|
427
|
+
|
|
319
428
|
vault_client = _VaultClient(
|
|
320
429
|
auth_type="gcp",
|
|
321
|
-
gcp_keyfile_dict={"
|
|
430
|
+
gcp_keyfile_dict={"client_email": "service_account_email"},
|
|
322
431
|
gcp_scopes="scope1,scope2",
|
|
432
|
+
role_id="role",
|
|
323
433
|
url="http://localhost:8180",
|
|
324
434
|
session=None,
|
|
325
435
|
)
|
|
326
|
-
|
|
327
|
-
|
|
436
|
+
|
|
437
|
+
# Preserve the original json.dumps
|
|
438
|
+
original_json_dumps = json.dumps
|
|
439
|
+
|
|
440
|
+
# Inject the mocked payload into the JWT signing process
|
|
441
|
+
with mock.patch("json.dumps") as mock_json_dumps:
|
|
442
|
+
|
|
443
|
+
def mocked_json_dumps(payload):
|
|
444
|
+
# Override the payload to inject controlled iat and exp values
|
|
445
|
+
payload["iat"] = iat
|
|
446
|
+
payload["exp"] = exp
|
|
447
|
+
return original_json_dumps(payload) # Use the original json.dumps
|
|
448
|
+
|
|
449
|
+
mock_json_dumps.side_effect = mocked_json_dumps
|
|
450
|
+
|
|
451
|
+
client = vault_client.client # Trigger the Vault client creation
|
|
452
|
+
|
|
453
|
+
# Assertions
|
|
454
|
+
mock_hvac_client.assert_called_with(url="http://localhost:8180", session=None)
|
|
328
455
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
329
456
|
mock_get_credentials.assert_called_with(
|
|
330
|
-
key_path=None, keyfile_dict={"
|
|
331
|
-
)
|
|
332
|
-
mock_hvac.Client.assert_called_with(url="http://localhost:8180", session=None)
|
|
333
|
-
client.auth.gcp.configure.assert_called_with(
|
|
334
|
-
credentials="credentials",
|
|
457
|
+
key_path=None, keyfile_dict={"client_email": "service_account_email"}, scopes=["scope1", "scope2"]
|
|
335
458
|
)
|
|
459
|
+
# Extract the arguments passed to the mocked signJwt API
|
|
460
|
+
args, kwargs = mock_sign_jwt.call_args
|
|
461
|
+
payload = json.loads(kwargs["body"]["payload"])
|
|
462
|
+
|
|
463
|
+
# Assert iat and exp values are as expected
|
|
464
|
+
assert payload["iat"] == iat
|
|
465
|
+
assert payload["exp"] == exp
|
|
466
|
+
assert abs(payload["exp"] - (payload["iat"] + 3600)) < 10 # Validate exp is 3600 seconds after iat
|
|
467
|
+
|
|
468
|
+
client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt")
|
|
336
469
|
client.is_authenticated.assert_called_with()
|
|
337
470
|
assert vault_client.kv_engine_version == 2
|
|
338
471
|
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import re
|
|
20
20
|
from unittest import mock
|
|
21
|
-
from unittest.mock import PropertyMock, mock_open, patch
|
|
21
|
+
from unittest.mock import MagicMock, PropertyMock, mock_open, patch
|
|
22
22
|
|
|
23
23
|
import pytest
|
|
24
24
|
from hvac.exceptions import VaultError
|
|
@@ -431,7 +431,10 @@ class TestVaultHook:
|
|
|
431
431
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
432
432
|
@mock.patch("airflow.providers.hashicorp.hooks.vault.VaultHook.get_connection")
|
|
433
433
|
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
434
|
-
|
|
434
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
435
|
+
def test_gcp_init_params(
|
|
436
|
+
self, mock_build, mock_hvac, mock_get_connection, mock_get_credentials, mock_get_scopes
|
|
437
|
+
):
|
|
435
438
|
mock_client = mock.MagicMock()
|
|
436
439
|
mock_hvac.Client.return_value = mock_client
|
|
437
440
|
mock_connection = self.get_mock_connection()
|
|
@@ -439,6 +442,17 @@ class TestVaultHook:
|
|
|
439
442
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
440
443
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
441
444
|
|
|
445
|
+
# Mock googleapiclient.discovery.build chain
|
|
446
|
+
mock_service = MagicMock()
|
|
447
|
+
mock_projects = MagicMock()
|
|
448
|
+
mock_service_accounts = MagicMock()
|
|
449
|
+
mock_sign_jwt = MagicMock()
|
|
450
|
+
mock_sign_jwt.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
451
|
+
mock_service_accounts.signJwt.return_value = mock_sign_jwt
|
|
452
|
+
mock_projects.serviceAccounts.return_value = mock_service_accounts
|
|
453
|
+
mock_service.projects.return_value = mock_projects
|
|
454
|
+
mock_build.return_value = mock_service
|
|
455
|
+
|
|
442
456
|
connection_dict = {}
|
|
443
457
|
|
|
444
458
|
mock_connection.extra_dejson.get.side_effect = connection_dict.get
|
|
@@ -447,20 +461,24 @@ class TestVaultHook:
|
|
|
447
461
|
"auth_type": "gcp",
|
|
448
462
|
"gcp_key_path": "path.json",
|
|
449
463
|
"gcp_scopes": "scope1,scope2",
|
|
464
|
+
"role_id": "role",
|
|
450
465
|
"session": None,
|
|
451
466
|
}
|
|
452
467
|
|
|
453
|
-
|
|
454
|
-
|
|
468
|
+
with patch(
|
|
469
|
+
"builtins.open", mock_open(read_data='{"client_email": "service_account_email"}')
|
|
470
|
+
) as mock_file:
|
|
471
|
+
test_hook = VaultHook(**kwargs)
|
|
472
|
+
test_client = test_hook.get_conn()
|
|
473
|
+
mock_file.assert_called_with("path.json")
|
|
474
|
+
|
|
455
475
|
mock_get_connection.assert_called_with("vault_conn_id")
|
|
456
476
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
457
477
|
mock_get_credentials.assert_called_with(
|
|
458
478
|
key_path="path.json", keyfile_dict=None, scopes=["scope1", "scope2"]
|
|
459
479
|
)
|
|
460
480
|
mock_hvac.Client.assert_called_with(url="http://localhost:8180", session=None)
|
|
461
|
-
test_client.auth.gcp.
|
|
462
|
-
credentials="credentials",
|
|
463
|
-
)
|
|
481
|
+
test_client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt")
|
|
464
482
|
test_client.is_authenticated.assert_called_with()
|
|
465
483
|
assert test_hook.vault_client.kv_engine_version == 2
|
|
466
484
|
|
|
@@ -468,7 +486,10 @@ class TestVaultHook:
|
|
|
468
486
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
469
487
|
@mock.patch("airflow.providers.hashicorp.hooks.vault.VaultHook.get_connection")
|
|
470
488
|
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
471
|
-
|
|
489
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
490
|
+
def test_gcp_dejson(
|
|
491
|
+
self, mock_build, mock_hvac, mock_get_connection, mock_get_credentials, mock_get_scopes
|
|
492
|
+
):
|
|
472
493
|
mock_client = mock.MagicMock()
|
|
473
494
|
mock_hvac.Client.return_value = mock_client
|
|
474
495
|
mock_connection = self.get_mock_connection()
|
|
@@ -476,29 +497,45 @@ class TestVaultHook:
|
|
|
476
497
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
477
498
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
478
499
|
|
|
500
|
+
# Mock googleapiclient.discovery.build chain
|
|
501
|
+
mock_service = MagicMock()
|
|
502
|
+
mock_projects = MagicMock()
|
|
503
|
+
mock_service_accounts = MagicMock()
|
|
504
|
+
mock_sign_jwt = MagicMock()
|
|
505
|
+
mock_sign_jwt.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
506
|
+
mock_service_accounts.signJwt.return_value = mock_sign_jwt
|
|
507
|
+
mock_projects.serviceAccounts.return_value = mock_service_accounts
|
|
508
|
+
mock_service.projects.return_value = mock_projects
|
|
509
|
+
mock_build.return_value = mock_service
|
|
510
|
+
|
|
479
511
|
connection_dict = {
|
|
480
512
|
"auth_type": "gcp",
|
|
481
513
|
"gcp_key_path": "path.json",
|
|
482
514
|
"gcp_scopes": "scope1,scope2",
|
|
515
|
+
"role_id": "role",
|
|
483
516
|
}
|
|
484
517
|
|
|
485
518
|
mock_connection.extra_dejson.get.side_effect = connection_dict.get
|
|
486
519
|
kwargs = {
|
|
487
520
|
"vault_conn_id": "vault_conn_id",
|
|
488
521
|
"session": None,
|
|
522
|
+
"role_id": "role",
|
|
489
523
|
}
|
|
490
524
|
|
|
491
|
-
|
|
492
|
-
|
|
525
|
+
with patch(
|
|
526
|
+
"builtins.open", mock_open(read_data='{"client_email": "service_account_email"}')
|
|
527
|
+
) as mock_file:
|
|
528
|
+
test_hook = VaultHook(**kwargs)
|
|
529
|
+
test_client = test_hook.get_conn()
|
|
530
|
+
mock_file.assert_called_with("path.json")
|
|
531
|
+
|
|
493
532
|
mock_get_connection.assert_called_with("vault_conn_id")
|
|
494
533
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
495
534
|
mock_get_credentials.assert_called_with(
|
|
496
535
|
key_path="path.json", keyfile_dict=None, scopes=["scope1", "scope2"]
|
|
497
536
|
)
|
|
498
537
|
mock_hvac.Client.assert_called_with(url="http://localhost:8180", session=None)
|
|
499
|
-
test_client.auth.gcp.
|
|
500
|
-
credentials="credentials",
|
|
501
|
-
)
|
|
538
|
+
test_client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt")
|
|
502
539
|
test_client.is_authenticated.assert_called_with()
|
|
503
540
|
assert test_hook.vault_client.kv_engine_version == 2
|
|
504
541
|
|
|
@@ -506,7 +543,10 @@ class TestVaultHook:
|
|
|
506
543
|
@mock.patch("airflow.providers.google.cloud.utils.credentials_provider.get_credentials_and_project_id")
|
|
507
544
|
@mock.patch("airflow.providers.hashicorp.hooks.vault.VaultHook.get_connection")
|
|
508
545
|
@mock.patch("airflow.providers.hashicorp._internal_client.vault_client.hvac")
|
|
509
|
-
|
|
546
|
+
@mock.patch("googleapiclient.discovery.build")
|
|
547
|
+
def test_gcp_dict_dejson(
|
|
548
|
+
self, mock_build, mock_hvac, mock_get_connection, mock_get_credentials, mock_get_scopes
|
|
549
|
+
):
|
|
510
550
|
mock_client = mock.MagicMock()
|
|
511
551
|
mock_hvac.Client.return_value = mock_client
|
|
512
552
|
mock_connection = self.get_mock_connection()
|
|
@@ -514,16 +554,29 @@ class TestVaultHook:
|
|
|
514
554
|
mock_get_scopes.return_value = ["scope1", "scope2"]
|
|
515
555
|
mock_get_credentials.return_value = ("credentials", "project_id")
|
|
516
556
|
|
|
557
|
+
# Mock googleapiclient.discovery.build chain
|
|
558
|
+
mock_service = MagicMock()
|
|
559
|
+
mock_projects = MagicMock()
|
|
560
|
+
mock_service_accounts = MagicMock()
|
|
561
|
+
mock_sign_jwt = MagicMock()
|
|
562
|
+
mock_sign_jwt.execute.return_value = {"signedJwt": "mocked_jwt"}
|
|
563
|
+
mock_service_accounts.signJwt.return_value = mock_sign_jwt
|
|
564
|
+
mock_projects.serviceAccounts.return_value = mock_service_accounts
|
|
565
|
+
mock_service.projects.return_value = mock_projects
|
|
566
|
+
mock_build.return_value = mock_service
|
|
567
|
+
|
|
517
568
|
connection_dict = {
|
|
518
569
|
"auth_type": "gcp",
|
|
519
|
-
"gcp_keyfile_dict": '{"
|
|
570
|
+
"gcp_keyfile_dict": '{"client_email": "service_account_email"}',
|
|
520
571
|
"gcp_scopes": "scope1,scope2",
|
|
572
|
+
"role_id": "role",
|
|
521
573
|
}
|
|
522
574
|
|
|
523
575
|
mock_connection.extra_dejson.get.side_effect = connection_dict.get
|
|
524
576
|
kwargs = {
|
|
525
577
|
"vault_conn_id": "vault_conn_id",
|
|
526
578
|
"session": None,
|
|
579
|
+
"role_id": "role",
|
|
527
580
|
}
|
|
528
581
|
|
|
529
582
|
test_hook = VaultHook(**kwargs)
|
|
@@ -531,12 +584,10 @@ class TestVaultHook:
|
|
|
531
584
|
mock_get_connection.assert_called_with("vault_conn_id")
|
|
532
585
|
mock_get_scopes.assert_called_with("scope1,scope2")
|
|
533
586
|
mock_get_credentials.assert_called_with(
|
|
534
|
-
key_path=None, keyfile_dict={"
|
|
587
|
+
key_path=None, keyfile_dict={"client_email": "service_account_email"}, scopes=["scope1", "scope2"]
|
|
535
588
|
)
|
|
536
589
|
mock_hvac.Client.assert_called_with(url="http://localhost:8180", session=None)
|
|
537
|
-
test_client.auth.gcp.
|
|
538
|
-
credentials="credentials",
|
|
539
|
-
)
|
|
590
|
+
test_client.auth.gcp.login.assert_called_with(role="role", jwt="mocked_jwt")
|
|
540
591
|
test_client.is_authenticated.assert_called_with()
|
|
541
592
|
assert test_hook.vault_client.kv_engine_version == 2
|
|
542
593
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|