Skip to content

Commit

Permalink
Merge pull request #3260 from vkarak/enhancement/change-db-file-mode
Browse files Browse the repository at this point in the history
[enhancement] Allow setting the file mode of the SQLite database
  • Loading branch information
vkarak authored Sep 17, 2024
2 parents 0bd2163 + 2b97cb7 commit ea93a5c
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 7 deletions.
10 changes: 10 additions & 0 deletions docs/config_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1651,6 +1651,16 @@ Result storage configuration

The SQLite database file to use.

.. py:attribute:: storage.sqlite_db_file_mode
:required: No
:default: ``"644"``

The permissions of the SQLite database file in octal form.

The mode will only taken into account upon creation of the DB file.
Permissions of an existing DB file have to be changed manually.


General Configuration
=====================
Expand Down
15 changes: 15 additions & 0 deletions docs/manpage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,21 @@ Whenever an environment variable is associated with a configuration option, its
.. versionadded:: 4.7


.. envvar:: RFM_SQLITE_DB_FILE_MODE

The permissions of the SQLite database file in octal form.

.. table::
:align: left

================================== ==================
Associated command line option N/A
Associated configuration parameter :attr:`~config.storage.sqlite_db_file_mode`
================================== ==================

.. versionadded:: 4.7


.. envvar:: RFM_SYSLOG_ADDRESS

The address of the Syslog server to send performance logs.
Expand Down
8 changes: 8 additions & 0 deletions reframe/frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: BSD-3-Clause

import functools
import inspect
import itertools
import json
Expand Down Expand Up @@ -777,6 +778,13 @@ def main():
configvar='storage/sqlite_db_file',
help='DB file where the results database resides (SQLite backend)'
)
argparser.add_argument(
dest='sqlite_db_file_mode',
envvar='RFM_SQLITE_DB_FILE_MODE',
configvar='storage/sqlite_db_file_mode',
help='DB file permissions (SQLite backend)',
type=functools.partial(int, base=8)
)
argparser.add_argument(
dest='syslog_address',
envvar='RFM_SYSLOG_ADDRESS',
Expand Down
31 changes: 25 additions & 6 deletions reframe/frontend/reporting/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import re
import sqlite3
import sys
from filelock import FileLock

import reframe.utility.jsonext as jsonext
Expand Down Expand Up @@ -52,6 +53,13 @@ def __init__(self):
self.__db_file = os.path.join(
osext.expandvars(runtime().get_option('storage/0/sqlite_db_file'))
)
mode = runtime().get_option(
'storage/0/sqlite_db_file_mode'
)
if not isinstance(mode, int):
self.__db_file_mode = int(mode, base=8)
else:
self.__db_file_mode = mode

def _db_file(self):
prefix = os.path.dirname(self.__db_file)
Expand All @@ -78,6 +86,18 @@ def _db_connect(self, *args, **kwargs):
with getprofiler().time_region('sqlite connect'):
return sqlite3.connect(*args, **kwargs)

def _db_lock(self):
prefix = os.path.dirname(self.__db_file)
if sys.version_info >= (3, 7):
kwargs = {'mode': self.__db_file_mode}
else:
# Python 3.6 forces us to use an older filelock version that does
# not support file modes. File modes where introduced in
# filelock 3.10
kwargs = {}

return FileLock(os.path.join(prefix, '.db.lock'), **kwargs)

def _db_create(self):
clsname = type(self).__name__
getlogger().debug(
Expand All @@ -104,6 +124,8 @@ def _db_create(self):
'on testcases(job_completion_time_unix)')
conn.execute('CREATE TABLE IF NOT EXISTS metadata('
'schema_version TEXT)')
# Update DB file mode
os.chmod(self.__db_file, self.__db_file_mode)

def _db_schema_check(self):
with self._db_connect(self.__db_file) as conn:
Expand Down Expand Up @@ -164,9 +186,8 @@ def _db_store_report(self, conn, report, report_file_path):
return session_uuid

def store(self, report, report_file=None):
prefix = os.path.dirname(self.__db_file)
with self._db_connect(self._db_file()) as conn:
with FileLock(os.path.join(prefix, '.db.lock')):
with self._db_lock():
return self._db_store_report(conn, report, report_file)

@time_function
Expand Down Expand Up @@ -298,8 +319,7 @@ def fetch_session_json(self, uuid):
return jsonext.loads(results[0][0]) if results else {}

def _do_remove(self, uuid):
prefix = os.path.dirname(self.__db_file)
with FileLock(os.path.join(prefix, '.db.lock')):
with self._db_lock():
with self._db_connect(self._db_file()) as conn:
# Enable foreign keys for delete action to have cascade effect
conn.execute('PRAGMA foreign_keys = ON')
Expand All @@ -316,8 +336,7 @@ def _do_remove(self, uuid):

def _do_remove2(self, uuid):
'''Remove a session using the RETURNING keyword'''
prefix = os.path.dirname(self.__db_file)
with FileLock(os.path.join(prefix, '.db.lock')):
with self._db_lock():
with self._db_connect(self._db_file()) as conn:
# Enable foreign keys for delete action to have cascade effect
conn.execute('PRAGMA foreign_keys = ON')
Expand Down
4 changes: 3 additions & 1 deletion reframe/schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,8 @@
"properties": {
"backend": {"type": "string"},
"sqlite_conn_timeout": {"type": "number"},
"sqlite_db_file": {"type": "string"}
"sqlite_db_file": {"type": "string"},
"sqlite_db_file_mode": {"type": "string"}
}
}
}
Expand Down Expand Up @@ -627,6 +628,7 @@
"storage/backend": "sqlite",
"storage/sqlite_conn_timeout": 60,
"storage/sqlite_db_file": "${HOME}/.reframe/reports/results.db",
"storage/sqlite_db_file_mode": "644",
"systems/descr": "",
"systems/max_local_jobs": 8,
"systems/modules_system": "nomod",
Expand Down

0 comments on commit ea93a5c

Please sign in to comment.