Skip to content

Commit 2e30877

Browse files
authored
Merge pull request #74 from PySport/feature/gcp-secrets-manager
Add GCP Secret Manager support to SecretsManager
2 parents 3c77e1b + 64ca858 commit 2e30877

1 file changed

Lines changed: 41 additions & 2 deletions

File tree

ingestify/application/secrets_manager.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,34 @@
1111
class SecretsManager:
1212
def __init__(self):
1313
self._aws_client = None
14+
self._gcp_client = None
1415

1516
@property
1617
def aws_client(self):
1718
if not self._aws_client:
1819
self._aws_client = boto3.client("secretsmanager")
1920
return self._aws_client
2021

22+
@property
23+
def gcp_client(self):
24+
if not self._gcp_client:
25+
try:
26+
from google.cloud import secretmanager
27+
except ImportError as e:
28+
raise ConfigurationError(
29+
"google-cloud-secret-manager is required for vault+gcp:// "
30+
"secrets. Install with: pip install google-cloud-secret-manager"
31+
) from e
32+
self._gcp_client = secretmanager.SecretManagerServiceClient()
33+
return self._gcp_client
34+
2135
def load_as_dict(self, url: str) -> dict:
22-
"""Load a secret from the supported vault. In this case only AWS Secrets Manager"""
36+
"""Load a JSON secret from a supported vault.
37+
38+
Supported schemes:
39+
vault+aws://<secret_id> (AWS Secrets Manager)
40+
vault+gcp://<project>/<secret> (GCP Secret Manager)
41+
"""
2342
parts = urlparse(url)
2443
if parts.scheme == "vault+aws":
2544
secret_id = parts.netloc + parts.path
@@ -35,12 +54,32 @@ def load_as_dict(self, url: str) -> dict:
3554
except JSONDecodeError:
3655
raise Exception(f"Secret url '{url}' could not be parsed.")
3756

57+
elif parts.scheme == "vault+gcp":
58+
project = parts.netloc
59+
secret_name = parts.path.lstrip("/")
60+
if not project or not secret_name:
61+
raise ConfigurationError(
62+
f"Invalid GCP secret URL '{url}'. "
63+
f"Expected format: vault+gcp://<project>/<secret>"
64+
)
65+
name = f"projects/{project}/secrets/{secret_name}/versions/latest"
66+
try:
67+
response = self.gcp_client.access_secret_version(request={"name": name})
68+
except Exception as err:
69+
raise ConfigurationError(
70+
f"Couldn't load GCP secret '{url}': {err}"
71+
) from err
72+
try:
73+
secrets = json.loads(response.payload.data.decode("utf-8"))
74+
except JSONDecodeError:
75+
raise Exception(f"Secret url '{url}' could not be parsed.")
76+
3877
else:
3978
raise Exception(f"Secret url '{url}' is not supported.")
4079
return secrets
4180

4281
def supports(self, url: str):
43-
return url.startswith("vault+aws://")
82+
return url.startswith("vault+aws://") or url.startswith("vault+gcp://")
4483

4584
def load_as_db_url(self, secret_uri: str):
4685
"""Load the secret and return it as a database url."""

0 commit comments

Comments
 (0)