Skip to content

Commit e162185

Browse files
committed
storage(postgresql): add Postgres connector + config schema + dependency
- Introduce _PostgresConnector and wire backend='postgresql' in StorageBackend.create(). - Read connection parameters from site config: storage/0/postgresql_driver, postgresql_host, postgresql_port, postgresql_db, postgresql_conn_timeout and credentials from env: RFM_POSTGRES_USER / RFM_POSTGRES_PASSWORD. - Pass connect_timeout via DBAPI connect args. - Extend config schema (schemas/config.json) with the new Postgres options and add sensible defaults (driver=psycopg2, conn_timeout=60). - Keep SQLite as-is; no behavior change for existing users. - Add psycopg2-binary==2.9.8 to requirements.txt to provide the PG driver.
1 parent 6284f04 commit e162185

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

reframe/frontend/reporting/storage.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@
3434

3535

3636
class _ConnectionStrategy:
37-
'''Abstract helper class for building SQLAlchemy engine configurations'''
37+
'''Abstract helper class for building the URL and kwargs for a given SQL dialect'''
3838

3939
def __init__(self):
4040
self.url = self._build_connection_url()
4141
self.engine = create_engine(self.url, **self._connection_kwargs)
4242

4343
@abc.abstractmethod
4444
def _build_connection_url(self):
45-
'''Return a SQLAlchemy URL string for this backend.
45+
'''Return a SQLAlchemy URL string for this dialect.
4646
4747
Implementations must return a URL suitable for passing to
4848
`sqlalchemy.create_engine()`.
@@ -74,6 +74,7 @@ def __init__(self):
7474
os.makedirs(prefix, exist_ok=True)
7575

7676
open(self.__db_file, 'a').close()
77+
# Update DB file mode
7778
os.chmod(self.__db_file, self.__db_file_mode)
7879

7980
super().__init__()
@@ -98,6 +99,33 @@ def _connection_kwargs(self):
9899
return {'connect_args': {'timeout': timeout}}
99100

100101

102+
class _PostgresConnector(_ConnectionStrategy):
103+
def __init__(self):
104+
super().__init__()
105+
106+
def _build_connection_url(self):
107+
host = runtime().get_option('storage/0/postgresql_host')
108+
port = runtime().get_option('storage/0/postgresql_port')
109+
db = runtime().get_option('storage/0/postgresql_db')
110+
driver = runtime().get_option('storage/0/postgresql_driver')
111+
user = os.getenv('RFM_POSTGRES_USER')
112+
password = os.getenv('RFM_POSTGRES_PASSWORD')
113+
if not (driver and host and port and db and user and password):
114+
raise ReframeError(
115+
'Postgres connection info must be set in config and env')
116+
117+
return URL.create(
118+
drivername=f'postgresql+{driver}',
119+
username=user, password=password,
120+
host=host, port=port, database=db
121+
).render_as_string(hide_password=False)
122+
123+
@property
124+
def _connection_kwargs(self):
125+
timeout = runtime().get_option('storage/0/postgresql_conn_timeout')
126+
return {'connect_args': {'connect_timeout': timeout}}
127+
128+
101129
class StorageBackend:
102130
'''Abstract class that represents the results backend storage'''
103131

@@ -106,6 +134,8 @@ def create(cls, backend, *args, **kwargs):
106134
'''Factory method for creating storage backends'''
107135
if backend == 'sqlite':
108136
return _SqlStorage(_SqliteConnector(), *args, **kwargs)
137+
elif backend == 'postgresql':
138+
return _SqlStorage(_PostgresConnector(), *args, **kwargs)
109139
else:
110140
raise ReframeError(f'no such storage backend: {backend}')
111141

reframe/schemas/config.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@
331331
"prepare_cmds": {
332332
"type": "array",
333333
"items": {"type": "string"}
334-
},
334+
},
335335
"processor": {"$ref": "#/defs/processor_info"},
336336
"devices": {"$ref": "#/defs/devices"},
337337
"features": {
@@ -560,6 +560,11 @@
560560
"sqlite_conn_timeout": {"type": "number"},
561561
"sqlite_db_file": {"type": "string"},
562562
"sqlite_db_file_mode": {"type": "string"},
563+
"postgresql_driver": {"type": "string"},
564+
"postgresql_host": {"type": "string"},
565+
"postgresql_port": {"type": "number"},
566+
"postgresql_db": {"type": "string"},
567+
"postgresql_conn_timeout": {"type": "number"},
563568
"target_systems": {"$ref": "#/defs/system_ref"}
564569
}
565570
}
@@ -654,6 +659,8 @@
654659
"storage/sqlite_conn_timeout": 60,
655660
"storage/sqlite_db_file": "${HOME}/.reframe/reports/results.db",
656661
"storage/sqlite_db_file_mode": "644",
662+
"storage/postgresql_conn_timeout": 60,
663+
"storage/postgresql_driver": "psycopg2",
657664
"storage/target_systems": ["*"],
658665
"systems/descr": "",
659666
"systems/max_local_jobs": 8,

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ tabulate==0.8.10; python_version == '3.6'
3636
tabulate==0.9.0; python_version >= '3.7'
3737
wcwidth==0.2.14
3838
sqlalchemy==2.0.41
39+
psycopg2-binary==2.9.8
3940
#+pygelf%pygelf==0.4.0

0 commit comments

Comments
 (0)