mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-07-22 11:59:33 +00:00
miniflux, ttrss: Factor out duplicated postgres utility methods
Tests: - Run functional tests on miniflux app. - Run backup and restore on ttrss app manually and notice that data is restored and there are no errors. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Joseph Nuthalapati <njoseph@riseup.net>
This commit is contained in:
parent
62b9063bc2
commit
df55ba8c61
50
plinth/db.py
50
plinth/db.py
@ -3,6 +3,8 @@
|
||||
Common utilities to help with handling a database.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import threading
|
||||
from typing import ClassVar
|
||||
|
||||
@ -83,3 +85,51 @@ class DBLock:
|
||||
# most of the significant cases where we have seen database lock issues.
|
||||
|
||||
lock = DBLock()
|
||||
|
||||
|
||||
#
|
||||
# PostgreSQL utilites
|
||||
#
|
||||
def _run_as_postgres(command, stdin=None, stdout=None):
|
||||
"""Run a command as postgres user."""
|
||||
command = ['sudo', '--user', 'postgres'] + command
|
||||
return subprocess.run(command, stdin=stdin, stdout=stdout, check=True)
|
||||
|
||||
|
||||
def postgres_dump_database(backup_file: str, database_name: str,
|
||||
database_user: str):
|
||||
"""Dump PostgreSQL database to a file.
|
||||
|
||||
Overwrites file if it exists. Uses pg_dump utility from postgres package
|
||||
(needs to be installed).
|
||||
"""
|
||||
backup_path = pathlib.Path(backup_file)
|
||||
backup_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(backup_path, 'w', encoding='utf-8') as file_handle:
|
||||
process = _run_as_postgres(['pg_dumpall', '--roles-only'],
|
||||
stdout=subprocess.PIPE)
|
||||
file_handle.write(f'DROP ROLE IF EXISTS {database_user};\n')
|
||||
for line in process.stdout.decode().splitlines():
|
||||
if database_user in line:
|
||||
file_handle.write(line + '\n')
|
||||
|
||||
with open(backup_path, 'a', encoding='utf-8') as file_handle:
|
||||
_run_as_postgres(
|
||||
['pg_dump', '--create', '--clean', '--if-exists', database_name],
|
||||
stdout=file_handle)
|
||||
|
||||
|
||||
def postgres_restore_database(backup_file: str, database_name):
|
||||
"""Restore PostgreSQL database from a file.
|
||||
|
||||
Drops database and recreates it. Uses pg_dump utility from postgres package
|
||||
(needs to be installed).
|
||||
"""
|
||||
# This is needed for old backups only. New backups include 'DROP DATABASE
|
||||
# IF EXISTS' and 'CREATE DATABASE' statements.
|
||||
_run_as_postgres(['dropdb', database_name])
|
||||
_run_as_postgres(['createdb', database_name])
|
||||
|
||||
with open(backup_file, 'r', encoding='utf-8') as file_handle:
|
||||
_run_as_postgres(['psql', '--dbname', database_name],
|
||||
stdin=file_handle)
|
||||
|
||||
@ -4,13 +4,12 @@
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
from typing import Tuple
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import pexpect
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth import action_utils, db
|
||||
from plinth.actions import privileged
|
||||
from plinth.utils import is_non_empty_file
|
||||
|
||||
@ -124,44 +123,16 @@ def _get_database_config():
|
||||
}
|
||||
|
||||
|
||||
# The following 3 methods are duplicated in tt-rss/privileged.py
|
||||
|
||||
|
||||
def _run_as_postgres(command, stdin=None, stdout=None):
|
||||
"""Run a command as postgres user."""
|
||||
command = ['sudo', '--user', 'postgres'] + command
|
||||
return subprocess.run(command, stdin=stdin, stdout=stdout, check=True)
|
||||
|
||||
|
||||
@privileged
|
||||
def dump_database():
|
||||
"""Dump database to file."""
|
||||
config = _get_database_config()
|
||||
os.makedirs(os.path.dirname(DB_BACKUP_FILE), exist_ok=True)
|
||||
with open(DB_BACKUP_FILE, 'w', encoding='utf-8') as db_backup_file:
|
||||
process = _run_as_postgres(['pg_dumpall', '--roles-only'],
|
||||
stdout=subprocess.PIPE)
|
||||
db_backup_file.write(f'DROP ROLE IF EXISTS {config["user"]};\n')
|
||||
for line in process.stdout.decode().splitlines():
|
||||
if config['user'] in line:
|
||||
db_backup_file.write(line + '\n')
|
||||
|
||||
with open(DB_BACKUP_FILE, 'a', encoding='utf-8') as db_backup_file:
|
||||
_run_as_postgres([
|
||||
'pg_dump', '--create', '--clean', '--if-exists', config['database']
|
||||
], stdout=db_backup_file)
|
||||
db.postgres_dump_database(DB_BACKUP_FILE, config['database'],
|
||||
config['user'])
|
||||
|
||||
|
||||
@privileged
|
||||
def restore_database():
|
||||
"""Restore database from file."""
|
||||
config = _get_database_config()
|
||||
|
||||
# This is needed for old backups only. New backups include 'DROP DATABASE
|
||||
# IF EXISTS' and 'CREATE DATABASE' statements.
|
||||
_run_as_postgres(['dropdb', config['database']])
|
||||
_run_as_postgres(['createdb', config['database']])
|
||||
|
||||
with open(DB_BACKUP_FILE, 'r', encoding='utf-8') as db_restore_file:
|
||||
_run_as_postgres(['psql', '--dbname', config['database']],
|
||||
stdin=db_restore_file)
|
||||
db.postgres_restore_database(DB_BACKUP_FILE, config['database'])
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Configure Tiny Tiny RSS."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import augeas
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth import action_utils, db
|
||||
from plinth.actions import privileged
|
||||
|
||||
CONFIG_FILE = '/etc/tt-rss/config.php'
|
||||
@ -124,40 +121,15 @@ def enable_api_access():
|
||||
def dump_database():
|
||||
"""Dump database to file."""
|
||||
config = _get_database_config()
|
||||
os.makedirs(os.path.dirname(DB_BACKUP_FILE), exist_ok=True)
|
||||
with open(DB_BACKUP_FILE, 'w', encoding='utf-8') as db_backup_file:
|
||||
process = _run_as_postgres(['pg_dumpall', '--roles-only'],
|
||||
stdout=subprocess.PIPE)
|
||||
db_backup_file.write(f'DROP ROLE IF EXISTS {config["user"]};\n')
|
||||
for line in process.stdout.decode().splitlines():
|
||||
if config['user'] in line:
|
||||
db_backup_file.write(line + '\n')
|
||||
|
||||
with open(DB_BACKUP_FILE, 'a', encoding='utf-8') as db_backup_file:
|
||||
_run_as_postgres([
|
||||
'pg_dump', '--create', '--clean', '--if-exists', config['database']
|
||||
], stdout=db_backup_file)
|
||||
db.postgres_dump_database(DB_BACKUP_FILE, config['database'],
|
||||
config['user'])
|
||||
|
||||
|
||||
@privileged
|
||||
def restore_database():
|
||||
"""Restore database from file."""
|
||||
config = _get_database_config()
|
||||
|
||||
# This is needed for old backups only. New backups include 'DROP DATABASE
|
||||
# IF EXISTS' and 'CREATE DATABASE' statements.
|
||||
_run_as_postgres(['dropdb', config['database']])
|
||||
_run_as_postgres(['createdb', config['database']])
|
||||
|
||||
with open(DB_BACKUP_FILE, 'r', encoding='utf-8') as db_restore_file:
|
||||
_run_as_postgres(['psql', '--dbname', config['database']],
|
||||
stdin=db_restore_file)
|
||||
|
||||
|
||||
def _run_as_postgres(command, stdin=None, stdout=None):
|
||||
"""Run a command as postgres user."""
|
||||
command = ['sudo', '--user', 'postgres'] + command
|
||||
return subprocess.run(command, stdin=stdin, stdout=stdout, check=True)
|
||||
db.postgres_restore_database(DB_BACKUP_FILE, config['database'])
|
||||
|
||||
|
||||
def load_augeas():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user