Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Environment variables:
- `TRACE_REDACT_KEYS` (optional) – comma separated message keys redacted from `TRACE` payload logs. Defaults to `password,secret,token,key,apikey,api_key`.
- `TRACE_MAX_BYTES` (optional) – maximum size of a logged `TRACE` payload. Defaults to `10000`.
- `CONF_DIR` (optional) – directory containing `config.json` and `access.json`. Defaults to `conf`.
- `POSTGRES_SECRET_NAME` (optional) – AWS Secrets Manager secret name holding PostgreSQL connection credentials (host, port, database, user, password). Required for Postgres writer and stats reader.
- `POSTGRES_SECRET_NAME` (optional) – AWS Secrets Manager secret name holding PostgreSQL connection credentials (host, port, database, user, password). Each Lambda should point at its own least-privilege role's secret (see `database/README.md` for the role/grant reference).
- `POSTGRES_SECRET_REGION` (optional) – AWS region of the Secrets Manager secret. Must be set together with `POSTGRES_SECRET_NAME`.

## Logging & Correlation
Expand Down Expand Up @@ -206,7 +206,7 @@ Configured via `kafka_bootstrap_server`. (Future: support auth properties / TLS
Publishes events to the configured `event_bus_arn` using put events API.

### Postgres Writer
Writes enriched event data (runs + jobs) into PostgreSQL tables. Connection credentials are loaded from AWS Secrets Manager using the `POSTGRES_SECRET_NAME` and `POSTGRES_SECRET_REGION` environment variables.
Writes enriched event data (runs + jobs) into PostgreSQL tables. Connection credentials are loaded from AWS Secrets Manager using the `POSTGRES_SECRET_NAME` and `POSTGRES_SECRET_REGION` environment variables, which should reference the `eventgate_writer` role's secret (see `database/README.md`).

## Troubleshooting
| Symptom | Possible Cause | Action |
Expand Down
7 changes: 6 additions & 1 deletion src/utils/postgres_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ def _get_connection(self) -> Any:
self._connection = psycopg2.connect(**connect_kwargs)
logger.info(
"New PostgreSQL connection established.",
extra={"database": pg_config["database"], "host": pg_config["host"], "port": pg_config["port"]},
extra={
"database": pg_config["database"],
"host": pg_config["host"],
"port": pg_config["port"],
"user": pg_config["user"],
},
)
return self._connection

Expand Down
33 changes: 21 additions & 12 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,23 +311,30 @@ def lambda_handler_factory(
os.environ["LOG_LEVEL"] = "DEBUG"
os.environ["AWS_DEFAULT_REGION"] = mock_aws_services["region"]

# Store PostgreSQL credentials in mocked Secrets Manager so WriterPostgres can find them.
# Store PostgreSQL credentials in mocked Secrets Manager so WriterPostgres/ReaderPostgres can
# find them. Mirror production: each Lambda gets its own secret for its own least-privilege
# role (eventgate_writer / eventgate_reader), created by Flyway's V1.4.0.1__create_roles.ddl.
parsed_dsn = urlparse(postgres_container)
pg_secret = {
"database": parsed_dsn.path.lstrip("/"),
"host": parsed_dsn.hostname,
"port": parsed_dsn.port,
"user": parsed_dsn.username,
"password": parsed_dsn.password,
}

def _pg_secret_for_role(role_user: str) -> str:
secret = {
"database": parsed_dsn.path.lstrip("/"),
"host": parsed_dsn.hostname,
"port": parsed_dsn.port,
"user": role_user,
"password": TEST_ROLE_PASSWORD,
}
return json.dumps(secret)

sm_client = boto3.client(
"secretsmanager",
region_name=mock_aws_services["region"],
)
sm_client.create_secret(Name="eventgate/postgres", SecretString=json.dumps(pg_secret))
os.environ["POSTGRES_SECRET_NAME"] = "eventgate/postgres"
sm_client.create_secret(Name="eventgate/writer", SecretString=_pg_secret_for_role("eventgate_writer"))
sm_client.create_secret(Name="eventgate/reader", SecretString=_pg_secret_for_role("eventgate_reader"))
os.environ["POSTGRES_SECRET_REGION"] = mock_aws_services["region"]
logger.debug("PostgreSQL secret stored in moto Secrets Manager.")
os.environ["POSTGRES_SECRET_NAME"] = "eventgate/writer"
logger.debug("PostgreSQL writer/reader secrets stored in moto Secrets Manager.")

# Create test config with container URLs.
test_config_dir = PROJECT_ROOT / "tests" / "integration" / ".tmp_conf"
Expand Down Expand Up @@ -406,7 +413,9 @@ def stats_lambda_handler(
``lambda_handler_factory`` to trigger that setup, then import the
stats Lambda module.
"""
# lambda_handler_factory has already set up the env; import stats Lambda.
# lambda_handler_factory has already set up the env, but pointed POSTGRES_SECRET_NAME at the
# eventgate_writer secret for WriterPostgres, so changing it now for the eventgate_reader instead.
os.environ["POSTGRES_SECRET_NAME"] = "eventgate/reader"
from src.event_stats_lambda import lambda_handler as stats_handler

return stats_handler
Expand Down
Loading