|
20 | 20 | Pydantic's dotenv will automagically populate it based on it |
21 | 21 | """ |
22 | 22 |
|
| 23 | +import logging |
23 | 24 | import os |
24 | 25 | import pathlib |
| 26 | + |
| 27 | +import boto3 |
| 28 | + |
| 29 | +from dotenv import dotenv_values |
| 30 | +from io import StringIO |
25 | 31 | from pydantic import BaseSettings, SecretStr |
26 | 32 |
|
27 | 33 | from brus_backend_common.helpers.uri import get_jdbc_url_from_pg_uri |
|
34 | 40 |
|
35 | 41 | ENV_FILE_PATH = os.path.join(_PROJECT_ROOT_DIR, ".env") |
36 | 42 |
|
| 43 | +logger = logging.getLogger(__name__) |
| 44 | + |
37 | 45 |
|
38 | 46 | class DefaultConfig(BaseSettings): |
39 | 47 | """Top-level config that defines all configuration variables, and their default, overridable values |
@@ -130,6 +138,10 @@ def AWS_S3_ENDPOINT(self): |
130 | 138 | def AWS_STS_ENDPOINT(self): |
131 | 139 | return f"sts.{self.AWS_REGION}.amazonaws.com" if not self.IS_LOCAL else f"{self.MINIO_HOST}:{self.MINIO_PORT}" |
132 | 140 |
|
| 141 | + @property |
| 142 | + def AWS_SSM_ENDPOINT(self): |
| 143 | + return f"ssm.{self.AWS_REGION}.amazonaws.com" if not self.IS_LOCAL else f"{self.MINIO_HOST}:{self.MINIO_PORT}" |
| 144 | + |
133 | 145 | # Buckets |
134 | 146 | DATA_ARCHIVE_BUCKET: str = "" |
135 | 147 | DATA_EXTRACTS_BUCKET: str = "" |
@@ -198,10 +210,51 @@ def JDBC_METASTORE_URL(self): |
198 | 210 | MINIO_DATA_DIR: str = "" |
199 | 211 |
|
200 | 212 |
|
| 213 | +# TODO: Update CONFIG to use BaseModel with nested environment variables which can help generate this list automatically |
| 214 | +# (ex. BUCKETS__DATA_ARCHIVE -> CONFIG.BUCKETS.DATA_ARCHIVE) |
| 215 | +CONFIG_BUCKETS = [attr for attr, value in DefaultConfig().__dict__.items() if attr.endswith("_BUCKET")] |
| 216 | + |
| 217 | + |
| 218 | +def pull_ssm_config() -> dict: |
| 219 | + """This function lives in the liminal space between CONFIG and helpers.aws, having a hand in both. |
| 220 | + While this is essentially more helpers.aws based, that file imports and uses CONFIG values from this file, |
| 221 | + which'd result in a circular dependency. |
| 222 | +
|
| 223 | + Likewise, we could re-use the helper function below but that'd also run into a circular dependency. |
| 224 | + ssm_client = _get_boto3('client', 'ssm') |
| 225 | + """ |
| 226 | + env_group = "prod" if CONFIG.ENV_CODE == "prod" else "nonprod" |
| 227 | + |
| 228 | + # TODO: Post-FAPC Cleanup |
| 229 | + non_fapc_path = f"/{env_group}/brus-backend-common/{CONFIG.ENV_CODE}/.env" |
| 230 | + fapc_path = "/kc-dtas/brus/broker/secrets" |
| 231 | + secrets_param_name = fapc_path if CONFIG.FAPC else non_fapc_path |
| 232 | + |
| 233 | + ssm_client = boto3.client("ssm", region_name=CONFIG.AWS_REGION) |
| 234 | + secrets_yaml_param = ssm_client.get_parameter(Name=secrets_param_name, WithDecryption=True) |
| 235 | + return dotenv_values(stream=StringIO(secrets_yaml_param["Parameter"]["Value"])) |
| 236 | + |
| 237 | + |
201 | 238 | CONFIG = DefaultConfig() |
202 | 239 |
|
203 | 240 |
|
204 | | -def set_brus_config(config): |
| 241 | +def set_brus_config(config: dict) -> None: |
205 | 242 | """Takes in a config dict of the attributes to override""" |
206 | 243 | for attr, value in config.items(): |
207 | 244 | setattr(CONFIG, attr, value) |
| 245 | + |
| 246 | + |
| 247 | +# Overwrite any values with ones pulled from SSM if not local |
| 248 | +# Note: DefaultConfig() can take the argument, but we need the initial default values to look up the right |
| 249 | +# Parameter values, so we're updating them after the initial pull. |
| 250 | +if not CONFIG.IS_LOCAL: |
| 251 | + ssm_config = pull_ssm_config() |
| 252 | + CONFIG = DefaultConfig(**ssm_config) |
| 253 | + |
| 254 | +# Overwrite any values if running in a test to prevent conflicting with your local environment |
| 255 | +# https://docs.pytest.org/en/stable/example/simple.html#detect-if-running-from-within-a-pytest-run |
| 256 | +if os.environ.get("PYTEST_VERSION") is not None: |
| 257 | + # Rename the buckets in the test environment to not overwrite your local buckets |
| 258 | + CONFIG = DefaultConfig( |
| 259 | + **{bucket_config: f"test-{getattr(CONFIG, bucket_config)}" for bucket_config in CONFIG_BUCKETS} |
| 260 | + ) |
0 commit comments