Fixed restore mode

This commit is contained in:
Antoine Nguyen
2022-11-09 10:30:44 +01:00
parent d6f9a5b913
commit 37633008cb
12 changed files with 103 additions and 63 deletions

60
.github/workflows/installer.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Modoboa installer
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r test-requirements.txt
- name: Run tests
if: ${{ matrix.python-version != '3.9' }}
run: |
python tests.py
- name: Run tests and coverage
if: ${{ matrix.python-version == '3.9' }}
run: |
coverage run tests.py
- name: Upload coverage result
if: ${{ matrix.python-version == '3.9' }}
uses: actions/upload-artifact@v2
with:
name: coverage-results
path: .coverage
coverage:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install codecov
- name: Download coverage results
uses: actions/download-artifact@v2
with:
name: coverage-results
- name: Report coverage
run: |
coverage report
codecov

View File

@@ -1,15 +0,0 @@
sudo: false
language: python
cache: pip
python:
- "2.7"
- "3.4"
before_install:
- pip install -r test-requirements.txt
script:
- coverage run tests.py
after_success:
- codecov

View File

@@ -1,7 +1,7 @@
modoboa-installer modoboa-installer
================= =================
|travis| |codecov| |workflow| |codecov|
An installer which deploy a complete mail server based on Modoboa. An installer which deploy a complete mail server based on Modoboa.
@@ -108,7 +108,7 @@ You can start the process as follows::
Then follow the step on the console. Then follow the step on the console.
There are also a non-interactive mode: There is also a non-interactive mode:
1. Silent mode 1. Silent mode
@@ -116,20 +116,16 @@ Command::
$ sudo ./run.py --silent-backup <your domain> $ sudo ./run.py --silent-backup <your domain>
This mode is the silent batch mode, when executed, it will create /modoboa_backup/ and each time you execute it, it will create a new backup directory with current date and time. This mode will run silently. When executed, it will create
/modoboa_backup/ and each time you execute it, it will create a new
backup directory with current date and time.
You can supply a custom path. You can supply a custom path if needed::
Command::
$ sudo ./run.py --silent-backup --backup-path /path/of/backup/directory <your domain> $ sudo ./run.py --silent-backup --backup-path /path/of/backup/directory <your domain>
This mode is the same as silent batch mode, but you provide the path to the backup directory you want. If you want to disable emails backup, disable dovecot in the
configuration file (set enabled to False).
If you want to disable mail backup::
$ sudo ./run.py {--backup|--silent-backup} --no-mail-backup <your domain>
This can be useful for larger instance. This can be useful for larger instance.
@@ -142,7 +138,7 @@ You can start the process as follows::
$ sudo ./run.py --restore /path/to/backup/directory/ <your domain> $ sudo ./run.py --restore /path/to/backup/directory/ <your domain>
Then wait for the process to finish Then wait for the process to finish.
Change the generated hostname Change the generated hostname
----------------------------- -----------------------------
@@ -187,7 +183,6 @@ modify the following settings::
Change the ``email`` setting to a valid value since it will be used Change the ``email`` setting to a valid value since it will be used
for account recovery. for account recovery.
.. |travis| image:: https://travis-ci.org/modoboa/modoboa-installer.png?branch=master .. |workflow| image:: https://github.com/modoboa/modoboa-installer/workflows/Modoboa%20installer/badge.svg
:target: https://travis-ci.org/modoboa/modoboa-installer
.. |codecov| image:: http://codecov.io/github/modoboa/modoboa-installer/coverage.svg?branch=master .. |codecov| image:: http://codecov.io/github/modoboa/modoboa-installer/coverage.svg?branch=master
:target: http://codecov.io/github/modoboa/modoboa-installer?branch=master :target: http://codecov.io/github/modoboa/modoboa-installer?branch=master

View File

@@ -17,7 +17,7 @@ def load_app_script(appname):
return script return script
def install(appname, config, upgrade, restore): def install(appname: str, config, upgrade: bool, archive_path: str):
"""Install an application.""" """Install an application."""
if (config.has_option(appname, "enabled") and if (config.has_option(appname, "enabled") and
not config.getboolean(appname, "enabled")): not config.getboolean(appname, "enabled")):
@@ -26,9 +26,9 @@ def install(appname, config, upgrade, restore):
utils.printcolor("Installing {}".format(appname), utils.MAGENTA) utils.printcolor("Installing {}".format(appname), utils.MAGENTA)
script = load_app_script(appname) script = load_app_script(appname)
try: try:
getattr(script, appname.capitalize())(config, upgrade, restore).run() getattr(script, appname.capitalize())(config, upgrade, archive_path).run()
except utils.FatalError as inst: except utils.FatalError as inst:
utils.printcolor(u"{}".format(inst), utils.RED) utils.error("{}".format(inst))
sys.exit(1) sys.exit(1)
@@ -43,7 +43,7 @@ def backup(appname, config, path):
try: try:
getattr(script, appname.capitalize())(config, False, False).backup(path) getattr(script, appname.capitalize())(config, False, False).backup(path)
except utils.FatalError as inst: except utils.FatalError as inst:
utils.printcolor(u"{}".format(inst), utils.RED) utils.error("{}".format(inst))
sys.exit(1) sys.exit(1)

View File

@@ -92,8 +92,8 @@ class Amavis(base.Installer):
def post_run(self): def post_run(self):
"""Additional tasks.""" """Additional tasks."""
install("spamassassin", self.config, self.upgrade, self.restore) install("spamassassin", self.config, self.upgrade, self.archive_path)
install("clamav", self.config, self.upgrade, self.restore) install("clamav", self.config, self.upgrade, self.archive_path)
def custom_backup(self, path): def custom_backup(self, path):
"""Backup custom configuration if any.""" """Backup custom configuration if any."""
@@ -109,7 +109,7 @@ class Amavis(base.Installer):
if package.backend.FORMAT != "deb": if package.backend.FORMAT != "deb":
return return
amavis_custom_configuration = os.path.join( amavis_custom_configuration = os.path.join(
self.restore, "custom/99-custom") self.archive_path, "custom/99-custom")
if os.path.isfile(amavis_custom_configuration): if os.path.isfile(amavis_custom_configuration):
utils.copy_file(amavis_custom_configuration, os.path.join( utils.copy_file(amavis_custom_configuration, os.path.join(
self.config_dir, "conf.d")) self.config_dir, "conf.d"))

View File

@@ -20,11 +20,11 @@ class Installer(object):
with_db = False with_db = False
config_files = [] config_files = []
def __init__(self, config, upgrade, restore): def __init__(self, config, upgrade: bool, archive_path: str):
"""Get configuration.""" """Get configuration."""
self.config = config self.config = config
self.upgrade = upgrade self.upgrade = upgrade
self.restore = restore self.archive_path = archive_path
if self.config.has_section(self.appname): if self.config.has_section(self.appname):
self.app_config = dict(self.config.items(self.appname)) self.app_config = dict(self.config.items(self.appname))
self.dbengine = self.config.get("database", "engine") self.dbengine = self.config.get("database", "engine")
@@ -61,7 +61,7 @@ class Installer(object):
utils.MAGENTA utils.MAGENTA
) )
database_backup_path = os.path.join( database_backup_path = os.path.join(
self.restore, f"databases/{self.appname}.sql") self.archive_path, f"databases/{self.appname}.sql")
if os.path.isfile(database_backup_path): if os.path.isfile(database_backup_path):
utils.success(f"SQL dump found in backup for {self.appname}!") utils.success(f"SQL dump found in backup for {self.appname}!")
return database_backup_path return database_backup_path
@@ -81,7 +81,7 @@ class Installer(object):
self.backend.create_user(self.dbuser, self.dbpasswd) self.backend.create_user(self.dbuser, self.dbpasswd)
self.backend.create_database(self.dbname, self.dbuser) self.backend.create_database(self.dbname, self.dbuser)
schema = None schema = None
if self.restore: if self.archive_path:
schema = self.get_sql_schema_from_backup() schema = self.get_sql_schema_from_backup()
if not schema: if not schema:
schema = self.get_sql_schema_path() schema = self.get_sql_schema_path()
@@ -188,9 +188,9 @@ class Installer(object):
if not self.upgrade: if not self.upgrade:
self.setup_database() self.setup_database()
self.install_config_files() self.install_config_files()
if self.restore:
self.restore()
self.post_run() self.post_run()
if self.archive_path:
self.restore()
self.restart_daemon() self.restart_daemon()
def _dump_database(self, backup_path: str): def _dump_database(self, backup_path: str):

View File

@@ -153,7 +153,7 @@ class Dovecot(base.Installer):
def restore(self): def restore(self):
"""Restore emails.""" """Restore emails."""
home_dir = self.config.get("dovecot", "home_dir") home_dir = self.config.get("dovecot", "home_dir")
mail_dir = os.path.join(self.restore, "mails/") mail_dir = os.path.join(self.archive_path, "mails/")
if len(os.listdir(mail_dir)) > 0: if len(os.listdir(mail_dir)) > 0:
utils.success("Copying mail backup over dovecot directory.") utils.success("Copying mail backup over dovecot directory.")
if os.path.exists(home_dir): if os.path.exists(home_dir):

View File

@@ -47,19 +47,7 @@ class Opendkim(base.Installer):
stat.S_IROTH | stat.S_IXOTH, stat.S_IROTH | stat.S_IXOTH,
target[1], target[2] target[1], target[2]
) )
# Restore dkim keys from backup if restoring super().install_config_files()
if self.restore is not None:
dkim_keys_backup = os.path.join(
self.restore, "custom/dkim")
if os.path.isdir(dkim_keys_backup):
for file in os.listdir(dkim_keys_backup):
file_path = os.path.join(dkim_keys_backup, file)
if os.path.isfile(file_path):
utils.copy_file(file_path, self.config.get(
"opendkim", "keys_storage_dir", fallback="/var/lib/dkim"))
utils.printcolor(
"DKIM keys restored from backup", utils.GREEN)
super(Opendkim, self).install_config_files()
def get_template_context(self): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
@@ -123,6 +111,18 @@ class Opendkim(base.Installer):
utils.exec_cmd( utils.exec_cmd(
"perl -pi -e '{}' /lib/systemd/system/opendkim.service".format(pattern)) "perl -pi -e '{}' /lib/systemd/system/opendkim.service".format(pattern))
def restore(self):
"""Restore keys."""
dkim_keys_backup = os.path.join(
self.archive_path, "custom/dkim")
if os.path.isdir(dkim_keys_backup):
for file in os.listdir(dkim_keys_backup):
file_path = os.path.join(dkim_keys_backup, file)
if os.path.isfile(file_path):
utils.copy_file(file_path, self.config.get(
"opendkim", "keys_storage_dir", fallback="/var/lib/dkim"))
utils.success("DKIM keys restored from backup")
def custom_backup(self, path): def custom_backup(self, path):
"""Backup DKIM keys.""" """Backup DKIM keys."""
storage_dir = self.config.get( storage_dir = self.config.get(

View File

@@ -97,7 +97,7 @@ class Postfix(base.Installer):
utils.exec_cmd("postalias {}".format(aliases_file)) utils.exec_cmd("postalias {}".format(aliases_file))
# Postwhite # Postwhite
install("postwhite", self.config, self.upgrade, self.restore) install("postwhite", self.config, self.upgrade, self.archive_path)
def backup(self, path): def backup(self, path):
"""Launch postwhite backup.""" """Launch postwhite backup."""

View File

@@ -61,7 +61,7 @@ class Postwhite(base.Installer):
def restore(self): def restore(self):
"""Restore config files.""" """Restore config files."""
postwhite_backup_configuration = os.path.join( postwhite_backup_configuration = os.path.join(
self.restore, "custom/postwhite.conf") self.archive_path, "custom/postwhite.conf")
if os.path.isfile(postwhite_backup_configuration): if os.path.isfile(postwhite_backup_configuration):
utils.copy_file(postwhite_backup_configuration, self.config_dir) utils.copy_file(postwhite_backup_configuration, self.config_dir)
utils.success("postwhite.conf restored from backup") utils.success("postwhite.conf restored from backup")

View File

@@ -26,7 +26,7 @@ class Radicale(base.Installer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Get configuration.""" """Get configuration."""
super(Radicale, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.venv_path = self.config.get("radicale", "venv_path") self.venv_path = self.config.get("radicale", "venv_path")
def _setup_venv(self): def _setup_venv(self):
@@ -76,7 +76,7 @@ class Radicale(base.Installer):
def restore(self): def restore(self):
"""Restore collections.""" """Restore collections."""
radicale_backup = os.path.join( radicale_backup = os.path.join(
self.restore, "custom/radicale") self.archive_path, "custom/radicale")
if os.path.isdir(radicale_backup): if os.path.isdir(radicale_backup):
restore_target = os.path.join(self.home_dir, "collections") restore_target = os.path.join(self.home_dir, "collections")
if os.path.isdir(restore_target): if os.path.isdir(restore_target):

2
run.py
View File

@@ -72,7 +72,7 @@ def backup_disclaimer():
def restore_disclaimer(): def restore_disclaimer():
"""Display restore disclamer. """ """Display restore disclamer. """
utils.printcolor( utils.printcolor(
"You are about to restore a previous installation of Modoboa." "You are about to restore a previous installation of Modoboa.\n"
"If a new version has been released in between, please update your database!", "If a new version has been released in between, please update your database!",
utils.BLUE) utils.BLUE)