https://github.com/christo-olivier/pydantic-google-secrets
Demonstrating how to extend Pydantic's BaseSettings and PydanticBaseSettingsSource classes to get values from Google Cloud Secret Manager
https://github.com/christo-olivier/pydantic-google-secrets
Last synced: 5 months ago
JSON representation
Demonstrating how to extend Pydantic's BaseSettings and PydanticBaseSettingsSource classes to get values from Google Cloud Secret Manager
- Host: GitHub
- URL: https://github.com/christo-olivier/pydantic-google-secrets
- Owner: christo-olivier
- License: mit
- Created: 2023-09-03T07:35:06.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-09-05T18:55:14.000Z (over 1 year ago)
- Last Synced: 2024-08-13T07:07:24.052Z (8 months ago)
- Language: Python
- Homepage:
- Size: 6.84 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - christo-olivier/pydantic-google-secrets - Demonstrating how to extend Pydantic's BaseSettings and PydanticBaseSettingsSource classes to get values from Google Cloud Secret Manager (Python)
README
# Overview
This repository shows how to extend the `PydanticBaseSettingsSource` class to allow you to get your
settings from Google Secret Manager.I have created a YouTube video that walks through the code in this repository. You can find it here:
[Settings management with Pydantic and Google Secret Manager](https://youtu.be/nSSoTRkEPLk)# How to use this repo
The application located in the `pydantic_google_secrets` folder can be run in the following ways:
1. From the command line with `python ./pydantic_google_secrets/app.py`
1. Using Docker, made easy with the included Makefile.
1. `make build` to build the image.
1. `make run-app-env-file` to run a container with the environment variables configured using the `.env` file.
1. `make run-app-env-var` to run a container with the environment variables configured as part of the docker run command.
1. `make run-app-adc` to run a container using Application Default Credentials (ADC) to authenticate to Google Cloud.# Code that is the focus of this repository
The code that is the focus of this repository is in the `pydantic_google_secrets/config.py` file. It is the `GoogleSecretManagerConfigSettingsSource` class that extends the `PydanticBaseSettingsSource` class and is subsequently used in our `Settings` class.
```python
class GoogleSecretManagerConfigSettingsSource(PydanticBaseSettingsSource):
"""
A settings class that loads settings from Google Secret Manager.The account under which the application is executed should have the
required access to Google Secret Manager.
"""def __init__(self, settings_cls: Type[BaseSettings]):
super().__init__(settings_cls)self._client = None
self._project_id = Nonedef _get_gsm_value(self, field_name: str) -> Optional[str]:
"""
Make the call to the Google Secret Manager API to get the value of the
secret.
"""
secret_name = self._client.secret_version_path(
project=self._project_id, secret=field_name, secret_version="latest"
)response = self._client.access_secret_version(name=secret_name)
return response.payload.data.decode("UTF-8")def get_field_value(
self, field: FieldInfo, field_name: str
) -> Tuple[Any, str, bool]:
"""
Get the value of a field from Google Secret Manager.
"""
try:
field_name = field.alias or field_name
field_value = self._get_gsm_value(field_name)
except (NotFound, PermissionDenied) as e:
logger.debug(e)
field_value = Nonereturn field_value, field_name, False
def __call__(self) -> Dict[str, Any]:
d: Dict[str, Any] = {}try:
# Set the credentials and project ID from the application default
# credentials
_credentials, project_id = gc_auth.default()
self._project_id = project_id
self._client = secretmanager.SecretManagerServiceClient(
credentials=_credentials
)for field_name, field in self.settings_cls.model_fields.items():
field_value, field_key, value_is_complex = self.get_field_value(
field, field_name
)
field_value = self.prepare_field_value(
field_name, field, field_value, value_is_complex
)
if field_value is not None:
d[field_key] = field_valueexcept DefaultCredentialsError as e:
logger.debug(e)return d
```