From ce67105c160c33e064755b5feb28c3332c408398 Mon Sep 17 00:00:00 2001 From: Eduardo Rios Date: Mon, 19 Oct 2026 16:06:17 -0300 Subject: [PATCH 1/3] fix(deps): sientia_do 1.12.5 so the Postgres engine works with SQLAlchemy 2.1 SQLAlchemy 2.1 makes psycopg (v3) the default driver for postgresql:// URLs. sientia_do 1.12.2 builds that URL and only psycopg2-binary is installed, so a fresh install (any pod restart) fails with "No module named 'psycopg'" and the gateway runtime does not start (no NATS write consumer). 1.12.5 uses postgresql+psycopg2:// explicitly (sientia-dataops-library c402a75). Co-Authored-By: Claude Opus 5.5 --- requirements.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index b6d8b89..3e2fd88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ asyncua redis -# TODO(SIENTIAPDE-1972): bump once sientia-dataops-library releases SIENTIAPDE-1590 -# (generic Mongo find_one/distinct/aggregate methods) — not in 1.12.2 yet. -# Local dev uses an editable install of the local checkout (feature/SIENTIAPDE-1590). -sientia_do==1.12.2 +# 1.12.5 builds the Postgres engine with an explicit postgresql+psycopg2:// URL: SQLAlchemy 2.1 +# defaults postgresql:// to psycopg (v3), which is not installed ("No module named 'psycopg'"). +sientia_do==1.12.5 prometheus_client pymongo nats-py>=2.6 From 279484b2aaa21ce4ad4313cceeb6388e9fa26fc6 Mon Sep 17 00:00:00 2001 From: Eduardo Rios Date: Mon, 19 Oct 2026 16:06:18 -0300 Subject: [PATCH 2/3] fix(logs): do not log the MongoDB credentials DataManager logged the full connection string, user and password included, at startup. Log it through redact_uri (mongodb://***@host:port/...). Co-Authored-By: Claude Opus 5.5 --- ingestor/managers/data_manager.py | 23 ++++++++++-- tests/unit/managers/test_data_manager.py | 45 +++++++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/ingestor/managers/data_manager.py b/ingestor/managers/data_manager.py index ef3b5ac..1b78380 100644 --- a/ingestor/managers/data_manager.py +++ b/ingestor/managers/data_manager.py @@ -12,6 +12,22 @@ from sientia_do.temporal.constants import now import ingestor.metrics as metrics +def redact_uri(uri: str) -> str: + """ + Returns ``uri`` without the user and password, safe to log + (``mongodb://user:pass@host:27017/db`` -> ``mongodb://***@host:27017/db``). + """ + prefix, sep, rest = uri.partition('://') + if not sep: + prefix, rest = '', uri + ends = [i for i in (rest.find('/'), rest.find('?')) if i != -1] + end = min(ends) if ends else len(rest) + authority, tail = rest[:end], rest[end:] + if '@' not in authority: + return uri + return f'{prefix}{sep}***@{authority.rpartition("@")[2]}{tail}' + + class DataManager(SientiaMonitoring): """ Manages data persistence operations for the OPC Ingestor. @@ -66,7 +82,8 @@ class DataManager(SientiaMonitoring): ) logger.info( - f'Trying to initializing DataManager with MongoDB servers: {mongo_connection_string}' + 'Trying to initializing DataManager with MongoDB servers: ' + f'{redact_uri(mongo_connection_string)}' ) self.connection_string = mongo_connection_string @@ -82,7 +99,9 @@ class DataManager(SientiaMonitoring): self.metadata = metadata - logger.info(f'DataManager initialized with MongoDB servers: {self.connection_string}') + logger.info( + f'DataManager initialized with MongoDB servers: {redact_uri(self.connection_string)}' + ) def shutdown(self): """ diff --git a/tests/unit/managers/test_data_manager.py b/tests/unit/managers/test_data_manager.py index 4c6b2b6..ecf58b8 100644 --- a/tests/unit/managers/test_data_manager.py +++ b/tests/unit/managers/test_data_manager.py @@ -1,9 +1,10 @@ from unittest.mock import ANY, AsyncMock, MagicMock, patch +import pytest from pytest import fixture, mark from sientia_do.notifications.models import NotificationLevel -from ingestor.managers.data_manager import DataManager +from ingestor.managers.data_manager import DataManager, redact_uri metadata = { 'metadata': { @@ -54,6 +55,48 @@ def test___init___success(mongodb_repository): assert data_manager.connection_string == 'mongodb://localhost:27017' +@patch('ingestor.managers.data_manager.MongoDBRepository') +def test___init___does_not_log_the_credentials(mongodb_repository): + logger_mock = MagicMock() + uri = 'mongodb://root:s3cr3t@mongodb.mongodb.svc.cluster.local:27017' + + data_manager = DataManager( + metadata=metadata['metadata'], + mongo_connection_string=uri, + mongo_database='sientia', + logger=logger_mock, + notification_handler=MagicMock(), + metrics_controller=MagicMock(), + ) + + logged = ' '.join(str(c) for c in logger_mock.info.call_args_list) + assert 's3cr3t' not in logged + assert 'root' not in logged + logger_mock.info.assert_any_call( + 'DataManager initialized with MongoDB servers: ' + 'mongodb://***@mongodb.mongodb.svc.cluster.local:27017' + ) + assert data_manager.connection_string == uri + + +@pytest.mark.parametrize( + ('uri', 'expected'), + [ + ('mongodb://localhost:27017', 'mongodb://localhost:27017'), + ( + 'mongodb://u:p@h1:27017,h2:27017/db?authSource=admin', + 'mongodb://***@h1:27017,h2:27017/db?authSource=admin', + ), + ('mongodb://u:p@h:27017?authSource=admin', 'mongodb://***@h:27017?authSource=admin'), + ('mongodb+srv://u@cluster.example.com', 'mongodb+srv://***@cluster.example.com'), + ('u:p@h:27017', '***@h:27017'), + ('mongodb://h:27017/db?x=a@b', 'mongodb://h:27017/db?x=a@b'), + ], +) +def test_redact_uri(uri, expected): + assert redact_uri(uri) == expected + + def test_shutdown(data_manager): close_mock = MagicMock() data_manager.mongo_repository.close = close_mock From 7ee09ee74323de92e66d9cc4d039f9875a80b03b Mon Sep 17 00:00:00 2001 From: Bruno Domingues Date: Wed, 21 Oct 2026 15:35:51 -0300 Subject: [PATCH 3/3] ci: trigger checks