From 335a676a1e3143c4ae494dda2913fb222d4f27f3 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 2 Mar 2023 20:28:55 +0100 Subject: [PATCH 1/8] Added ability to update configfile --- modoboa_installer/utils.py | 70 ++++++++++++++++++++++++++++++++++++-- run.py | 19 ++++++++++- test-requirements.txt | 1 - tests.py | 46 +++++++++++++++++++++++-- 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 5f97ccb..00317d1 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -320,8 +320,8 @@ def get_entry_value(entry, interactive): return user_value if user_value else default_value -def gen_config(dest, interactive=False): - """Create config file from dict template""" +def load_config_template(interactive): + """Instanciate a configParser object with the predefined template.""" tpl_dict = config_dict_template.ConfigDictTemplate config = configparser.ConfigParser() # only ask about options we need, else still generate default @@ -337,6 +337,72 @@ def gen_config(dest, interactive=False): for config_entry in section["values"]: value = get_entry_value(config_entry, interactive_section) config.set(section["name"], config_entry["option"], value) + return config + + +def update_config(path): + """Update an existing config file.""" + config = configparser.ConfigParser() + with open(path) as fp: + config.read_file(fp) + new_config = load_config_template(False) + + old_sections = config.sections() + new_sections = new_config.sections() + + update = False + + dropped_sections = list(set(old_sections) - set(new_sections)) + + if len(dropped_sections) > 0: + printcolor("Follow section(s) will not be ported " + "due to being deleted or renamed: " + + ', '.join(dropped_sections), + RED) + + for section in new_sections: + if section in old_sections: + new_options = new_config.options(section) + old_options = config.options(section) + + dropped_options = list(set(old_options) - set(new_options)) + + if len(dropped_options) > 0: + printcolor(f"Following option(s) from section: {section}, " + "will not be ported due to being " + "deleted or renamed: " + + ', '.join(dropped_options), + RED) + + for option in new_options: + if option in old_options: + value = config.get(section, option, raw=True) + if value != new_config.get(section, option, raw=True): + update = True + new_config.set(section, option, value) + + if update: + # Backing up old config file + date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + dest = f"{os.path.splitext(path)[0]}_{date}.old" + shutil.copy(path, dest) + + # Overwritting old config file + with open(path, "w") as configfile: + new_config.write(configfile) + + # Set file owner to running user and group, and set config file permission to 600 + current_username = getpass.getuser() + current_user = pwd.getpwnam(current_username) + os.chown(dest, current_user[2], current_user[3]) + os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR) + + return dest + + +def gen_config(dest, interactive=False): + """Create config file from dict template""" + config = load_config_template(interactive) with open(dest, "w") as configfile: config.write(configfile) diff --git a/run.py b/run.py index edd5720..5cd26ba 100755 --- a/run.py +++ b/run.py @@ -134,6 +134,10 @@ def main(input_args): parser.add_argument( "--stop-after-configfile-check", action="store_true", default=False, help="Check configuration, generate it if needed and exit") + parser.add_argument( + "--update-configfile", action="store_true", default=False, + help="Attempt to update the config file. " + "Installer will stop after performing the update.") parser.add_argument( "--interactive", action="store_true", default=False, help="Generate configuration file with user interaction") @@ -153,7 +157,8 @@ def main(input_args): parser.add_argument( "--silent-backup", action="store_true", default=False, help="For script usage, do not require user interaction " - "backup will be saved at ./modoboa_backup/Backup_M_Y_d_H_M if --backup-path is not provided") + "backup will be saved at ./modoboa_backup/Backup_M_Y_d_H_M " + "if --backup-path is not provided") parser.add_argument( "--restore", type=str, metavar="path", help="Restore a previously backup up modoboa instance on a NEW machine. " @@ -178,6 +183,18 @@ def main(input_args): sys.exit(1) utils.success("Welcome to Modoboa installer!\n") + + # Update configfile + if args.update_configfile: + backup_location = utils.update_config(args.configfile) + utils.printcolor("Update complete. It seems successful.", + utils.BLUE) + if backup_location is not None: + utils.printcolor("You will find your old config file " + f"here: {backup_location}", + utils.BLUE) + return + is_config_file_available = utils.check_config_file( args.configfile, args.interactive, args.upgrade, args.backup, is_restoring) diff --git a/test-requirements.txt b/test-requirements.txt index 55e3fa0..6ec2cdd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,2 @@ codecov mock -six diff --git a/tests.py b/tests.py index 201a252..55eabd1 100644 --- a/tests.py +++ b/tests.py @@ -6,8 +6,13 @@ import sys import tempfile import unittest -from six import StringIO -from six.moves import configparser +from io import StringIO +from pathlib import Path + +try: + import configparser +except ImportError: + import ConfigParser as configparser try: from unittest.mock import patch except ImportError: @@ -26,7 +31,11 @@ class ConfigFileTestCase(unittest.TestCase): def tearDown(self): """Delete temp dir.""" - shutil.rmtree(self.workdir) + out = StringIO() + sys.stdout = out + print(self.workdir) + #shutil.rmtree(self.workdir) + pass def test_configfile_generation(self): """Check simple case.""" @@ -57,6 +66,37 @@ class ConfigFileTestCase(unittest.TestCase): self.assertEqual(config.get("certificate", "type"), "self-signed") self.assertEqual(config.get("database", "engine"), "postgres") + def test_updating_configfile(self): + """Check configfile update mechanism.""" + cfgfile_temp = os.path.join(self.workdir, "installer_old.cfg") + + out = StringIO() + sys.stdout = out + run.main([ + "--stop-after-configfile-check", + "--configfile", cfgfile_temp, + "example.test"]) + self.assertTrue(os.path.exists(cfgfile_temp)) + + # Adding a dummy section + with open(cfgfile_temp, "a") as fp: + fp.write( +""" +[dummy] + weird_old_option = "hey +""") + print("here") + print(os.path.isfile(cfgfile_temp)) + + out = StringIO() + sys.stdout = out + run.main([ + "--update-configfile", + "--configfile", cfgfile_temp, + "example.test"]) + self.assertIn("dummy", out.getvalue()) + self.assertTrue(Path(self.workdir).glob("*.old")) + @patch("modoboa_installer.utils.user_input") def test_interactive_mode_letsencrypt(self, mock_user_input): """Check interactive mode.""" From dbfede6df1de470ca454c12b212086bfe5684766 Mon Sep 17 00:00:00 2001 From: Spitap Date: Fri, 3 Mar 2023 09:33:32 +0100 Subject: [PATCH 2/8] Fixed typo, updated test --- README.rst | 6 ++++++ modoboa_installer/utils.py | 2 +- tests.py | 6 +----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 5fa04ed..5a29a42 100644 --- a/README.rst +++ b/README.rst @@ -54,6 +54,12 @@ run the following command:: $ ./run.py --stop-after-configfile-check +If you updated your installer, your config file might be outdated. +The program will exit after the update, so you can check the config file +before continuing. To perform the update, run the following command:: + + $ ./run.py --update-configfile + An interactive mode is also available:: $ ./run.py --interactive diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 00317d1..2b614a8 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -355,7 +355,7 @@ def update_config(path): dropped_sections = list(set(old_sections) - set(new_sections)) if len(dropped_sections) > 0: - printcolor("Follow section(s) will not be ported " + printcolor("Following section(s) will not be ported " "due to being deleted or renamed: " + ', '.join(dropped_sections), RED) diff --git a/tests.py b/tests.py index 55eabd1..42ec1b7 100644 --- a/tests.py +++ b/tests.py @@ -31,11 +31,7 @@ class ConfigFileTestCase(unittest.TestCase): def tearDown(self): """Delete temp dir.""" - out = StringIO() - sys.stdout = out - print(self.workdir) - #shutil.rmtree(self.workdir) - pass + shutil.rmtree(self.workdir) def test_configfile_generation(self): """Check simple case.""" From 0b29f74e08a4cb93f49d4bd7e1a056a9ed9236bc Mon Sep 17 00:00:00 2001 From: Spitfireap <45575529+Spitfireap@users.noreply.github.com> Date: Sat, 11 Mar 2023 12:41:16 +0000 Subject: [PATCH 3/8] typo, review fix --- modoboa_installer/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 2b614a8..52fb371 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -321,7 +321,7 @@ def get_entry_value(entry, interactive): def load_config_template(interactive): - """Instanciate a configParser object with the predefined template.""" + """Instantiate a configParser object with the predefined template.""" tpl_dict = config_dict_template.ConfigDictTemplate config = configparser.ConfigParser() # only ask about options we need, else still generate default @@ -398,6 +398,7 @@ def update_config(path): os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR) return dest + return None def gen_config(dest, interactive=False): From 6261066ccd33febaa5b2537796fdd974a4bd8014 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 12 Mar 2023 00:30:04 +0100 Subject: [PATCH 4/8] Formating, force outdated config check --- README.rst | 6 ---- modoboa_installer/scripts/backup.py | 15 ++++------ modoboa_installer/scripts/base.py | 2 +- modoboa_installer/scripts/restore.py | 8 +++--- modoboa_installer/ssl.py | 2 +- modoboa_installer/utils.py | 42 ++++++++++++++++++---------- run.py | 39 +++++++++++++++----------- 7 files changed, 61 insertions(+), 53 deletions(-) diff --git a/README.rst b/README.rst index 5a29a42..5fa04ed 100644 --- a/README.rst +++ b/README.rst @@ -54,12 +54,6 @@ run the following command:: $ ./run.py --stop-after-configfile-check -If you updated your installer, your config file might be outdated. -The program will exit after the update, so you can check the config file -before continuing. To perform the update, run the following command:: - - $ ./run.py --update-configfile - An interactive mode is also available:: $ ./run.py --interactive diff --git a/modoboa_installer/scripts/backup.py b/modoboa_installer/scripts/backup.py index 1a6024c..285f9a5 100644 --- a/modoboa_installer/scripts/backup.py +++ b/modoboa_installer/scripts/backup.py @@ -44,8 +44,7 @@ class Backup: path_exists = os.path.exists(path) if path_exists and os.path.isfile(path): - utils.printcolor( - "Error, you provided a file instead of a directory!", utils.RED) + utils.error("Error, you provided a file instead of a directory!") return False if not path_exists: @@ -58,9 +57,7 @@ class Backup: utils.mkdir_safe(path, stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3]) else: - utils.printcolor( - "Error, backup directory not present.", utils.RED - ) + utils.error("Error, backup directory not present.") return False if len(os.listdir(path)) != 0: @@ -80,9 +77,7 @@ class Backup: shutil.rmtree(os.path.join(path, "databases"), ignore_errors=False) else: - utils.printcolor( - "Error: backup directory not clean.", utils.RED - ) + utils.error("Error: backup directory not clean.") return False self.backup_path = path @@ -131,8 +126,8 @@ class Backup: home_path = self.config.get("dovecot", "home_dir") if not os.path.exists(home_path) or os.path.isfile(home_path): - utils.printcolor("Error backing up Email, provided path " - f" ({home_path}) seems not right...", utils.RED) + utils.error("Error backing up Email, provided path " + f" ({home_path}) seems not right...") else: dst = os.path.join(self.backup_path, "mails/") diff --git a/modoboa_installer/scripts/base.py b/modoboa_installer/scripts/base.py index dea0897..104ba4f 100644 --- a/modoboa_installer/scripts/base.py +++ b/modoboa_installer/scripts/base.py @@ -131,7 +131,7 @@ class Installer(object): return exitcode, output = package.backend.install_many(packages) if exitcode: - utils.printcolor("Failed to install dependencies", utils.RED) + utils.error("Failed to install dependencies") sys.exit(1) def get_config_files(self): diff --git a/modoboa_installer/scripts/restore.py b/modoboa_installer/scripts/restore.py index 08e3abc..e109bfa 100644 --- a/modoboa_installer/scripts/restore.py +++ b/modoboa_installer/scripts/restore.py @@ -13,14 +13,14 @@ class Restore: """ if not os.path.isdir(restore): - utils.printcolor( - "Provided path is not a directory !", utils.RED) + utils.error( + "Provided path is not a directory !") sys.exit(1) modoba_sql_file = os.path.join(restore, "databases/modoboa.sql") if not os.path.isfile(modoba_sql_file): - utils.printcolor( - modoba_sql_file + " not found, please check your backup", utils.RED) + utils.error( + modoba_sql_file + " not found, please check your backup") sys.exit(1) # Everything seems alright here, proceeding... diff --git a/modoboa_installer/ssl.py b/modoboa_installer/ssl.py index bd2ef52..a76ef67 100644 --- a/modoboa_installer/ssl.py +++ b/modoboa_installer/ssl.py @@ -90,7 +90,7 @@ class LetsEncryptCertificate(CertificateBackend): elif "centos" in name: package.backend.install("certbot") else: - utils.printcolor("Failed to install certbot, aborting.", utils.RED) + utils.printcolor("Failed to install certbot, aborting.") sys.exit(1) # Nginx plugin certbot if ( diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 52fb371..99540b1 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -177,7 +177,7 @@ def check_config_file(dest, interactive=False, upgrade=False, backup=False, rest """Create a new installer config file if needed.""" is_present = True if os.path.exists(dest): - return is_present + return is_present, update_config(dest, False) if upgrade: printcolor( "You cannot upgrade an existing installation without a " @@ -198,7 +198,7 @@ def check_config_file(dest, interactive=False, upgrade=False, backup=False, rest "Configuration file {} not found, creating new one." .format(dest), YELLOW) gen_config(dest, interactive) - return is_present + return is_present, None def has_colours(stream): @@ -340,7 +340,7 @@ def load_config_template(interactive): return config -def update_config(path): +def update_config(path, apply_update=True): """Update an existing config file.""" config = configparser.ConfigParser() with open(path) as fp: @@ -353,35 +353,41 @@ def update_config(path): update = False dropped_sections = list(set(old_sections) - set(new_sections)) - - if len(dropped_sections) > 0: + added_sections = list(set(new_sections) - set(old_sections)) + if len(dropped_sections) > 0 and not apply_update: printcolor("Following section(s) will not be ported " "due to being deleted or renamed: " + ', '.join(dropped_sections), RED) + if len(dropped_sections) + len(added_sections) > 0: + update = True + for section in new_sections: if section in old_sections: new_options = new_config.options(section) old_options = config.options(section) dropped_options = list(set(old_options) - set(new_options)) - - if len(dropped_options) > 0: + added_options = list(set(new_options) - set(old_sections)) + if len(dropped_options) > 0 and not apply_update: printcolor(f"Following option(s) from section: {section}, " "will not be ported due to being " "deleted or renamed: " + ', '.join(dropped_options), RED) + if len(dropped_options) + len(added_options) > 0: + update = True - for option in new_options: - if option in old_options: - value = config.get(section, option, raw=True) - if value != new_config.get(section, option, raw=True): - update = True - new_config.set(section, option, value) + if apply_update: + for option in new_options: + if option in old_options: + value = config.get(section, option, raw=True) + if value != new_config.get(section, option, raw=True): + update = True + new_config.set(section, option, value) - if update: + if update and apply_update: # Backing up old config file date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S") dest = f"{os.path.splitext(path)[0]}_{date}.old" @@ -391,13 +397,19 @@ def update_config(path): with open(path, "w") as configfile: new_config.write(configfile) - # Set file owner to running user and group, and set config file permission to 600 + # Set file owner to running u+g, and set config file permission to 600 current_username = getpass.getuser() current_user = pwd.getpwnam(current_username) os.chown(dest, current_user[2], current_user[3]) os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR) return dest + elif update and not apply_update: + # Simply check if current config file is outdated + return True + elif not update and not apply_update: + return False + return None diff --git a/run.py b/run.py index 5cd26ba..b23be66 100755 --- a/run.py +++ b/run.py @@ -116,6 +116,15 @@ def backup_system(config, args): scripts.backup(app, config, backup_path) +def config_file_update_complete(backup_location): + utils.printcolor("Update complete. It seems successful.", + utils.BLUE) + if backup_location is not None: + utils.printcolor("You will find your old config file " + f"here: {backup_location}", + utils.BLUE) + + def main(input_args): """Install process.""" parser = argparse.ArgumentParser() @@ -134,10 +143,6 @@ def main(input_args): parser.add_argument( "--stop-after-configfile-check", action="store_true", default=False, help="Check configuration, generate it if needed and exit") - parser.add_argument( - "--update-configfile", action="store_true", default=False, - help="Attempt to update the config file. " - "Installer will stop after performing the update.") parser.add_argument( "--interactive", action="store_true", default=False, help="Generate configuration file with user interaction") @@ -184,18 +189,7 @@ def main(input_args): utils.success("Welcome to Modoboa installer!\n") - # Update configfile - if args.update_configfile: - backup_location = utils.update_config(args.configfile) - utils.printcolor("Update complete. It seems successful.", - utils.BLUE) - if backup_location is not None: - utils.printcolor("You will find your old config file " - f"here: {backup_location}", - utils.BLUE) - return - - is_config_file_available = utils.check_config_file( + is_config_file_available, outdate_config = utils.check_config_file( args.configfile, args.interactive, args.upgrade, args.backup, is_restoring) if not is_config_file_available and ( @@ -203,6 +197,19 @@ def main(input_args): utils.error("No config file found.") return + # Check if config is outdated and ask user if it needs to be updated + if is_config_file_available and outdate_config: + answer = utils.user_input("It seems that your config file is outdated. " + "Would you like to update it? (Y/n) ") + if answer.lower().startswith("y"): + config_file_update_complete(utils.update_config(args.configfile)) + answer = utils.user_input("Would you like to stop to review the updated config? (Y/n)") + if answer.lower().startswith("y"): + return + else: + utils.error("You might encounter unexpected errors ! " + "Make sur to update your config before opening an issue!") + if args.stop_after_configfile_check: return From 4cd3937fdddd3aa59dcc227095504a463d1c3e27 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 12 Mar 2023 00:50:34 +0100 Subject: [PATCH 5/8] Updated tests --- modoboa_installer/utils.py | 6 +++--- tests.py | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 99540b1..2003534 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -354,7 +354,7 @@ def update_config(path, apply_update=True): dropped_sections = list(set(old_sections) - set(new_sections)) added_sections = list(set(new_sections) - set(old_sections)) - if len(dropped_sections) > 0 and not apply_update: + if len(dropped_sections) > 0 and apply_update: printcolor("Following section(s) will not be ported " "due to being deleted or renamed: " + ', '.join(dropped_sections), @@ -369,8 +369,8 @@ def update_config(path, apply_update=True): old_options = config.options(section) dropped_options = list(set(old_options) - set(new_options)) - added_options = list(set(new_options) - set(old_sections)) - if len(dropped_options) > 0 and not apply_update: + added_options = list(set(new_options) - set(old_options)) + if len(dropped_options) > 0 and apply_update: printcolor(f"Following option(s) from section: {section}, " "will not be ported due to being " "deleted or renamed: " + diff --git a/tests.py b/tests.py index 42ec1b7..d3e2579 100644 --- a/tests.py +++ b/tests.py @@ -18,6 +18,7 @@ try: except ImportError: from mock import patch +from modoboa_installer import utils import run @@ -81,15 +82,9 @@ class ConfigFileTestCase(unittest.TestCase): [dummy] weird_old_option = "hey """) - print("here") - print(os.path.isfile(cfgfile_temp)) - out = StringIO() sys.stdout = out - run.main([ - "--update-configfile", - "--configfile", cfgfile_temp, - "example.test"]) + utils.update_config(cfgfile_temp) self.assertIn("dummy", out.getvalue()) self.assertTrue(Path(self.workdir).glob("*.old")) @@ -128,6 +123,9 @@ class ConfigFileTestCase(unittest.TestCase): " postwhite spamassassin uwsgi", out.getvalue() ) + self.assertNotIn("It seems that your config file is outdated.", + out.getvalue() + ) @patch("modoboa_installer.utils.user_input") def test_upgrade_mode(self, mock_user_input): From 52bccf33934da0145c65a709c235900f34d3d314 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 12 Mar 2023 10:22:40 +0100 Subject: [PATCH 6/8] Refactoring --- modoboa_installer/utils.py | 41 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 2003534..426708c 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -386,32 +386,31 @@ def update_config(path, apply_update=True): if value != new_config.get(section, option, raw=True): update = True new_config.set(section, option, value) + if apply_update: + if update: + # Backing up old config file + date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + dest = f"{os.path.splitext(path)[0]}_{date}.old" + shutil.copy(path, dest) - if update and apply_update: - # Backing up old config file - date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S") - dest = f"{os.path.splitext(path)[0]}_{date}.old" - shutil.copy(path, dest) + # Overwritting old config file + with open(path, "w") as configfile: + new_config.write(configfile) - # Overwritting old config file - with open(path, "w") as configfile: - new_config.write(configfile) + # Set file owner to running u+g, and set config file permission to 600 + current_username = getpass.getuser() + current_user = pwd.getpwnam(current_username) + os.chown(dest, current_user[2], current_user[3]) + os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR) - # Set file owner to running u+g, and set config file permission to 600 - current_username = getpass.getuser() - current_user = pwd.getpwnam(current_username) - os.chown(dest, current_user[2], current_user[3]) - os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR) - - return dest - elif update and not apply_update: - # Simply check if current config file is outdated - return True - elif not update and not apply_update: + return dest + return None + else: + if update: + # Simply check if current config file is outdated + return True return False - return None - def gen_config(dest, interactive=False): """Create config file from dict template""" From 85652320b60146b201f688a811c1c370c08103a8 Mon Sep 17 00:00:00 2001 From: Spitap Date: Mon, 13 Mar 2023 12:09:11 +0100 Subject: [PATCH 7/8] Simplified return --- modoboa_installer/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 426708c..ded7e7d 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -406,10 +406,8 @@ def update_config(path, apply_update=True): return dest return None else: - if update: - # Simply check if current config file is outdated - return True - return False + # Simply check if current config file is outdated + return update def gen_config(dest, interactive=False): From 602405833cde33c43e1d6353c378a689ed967334 Mon Sep 17 00:00:00 2001 From: Spitap Date: Mon, 13 Mar 2023 14:59:30 +0100 Subject: [PATCH 8/8] Better test --- run.py | 7 ++++--- tests.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/run.py b/run.py index b23be66..269c1d3 100755 --- a/run.py +++ b/run.py @@ -203,9 +203,10 @@ def main(input_args): "Would you like to update it? (Y/n) ") if answer.lower().startswith("y"): config_file_update_complete(utils.update_config(args.configfile)) - answer = utils.user_input("Would you like to stop to review the updated config? (Y/n)") - if answer.lower().startswith("y"): - return + if not args.stop_after_configfile_check: + answer = utils.user_input("Would you like to stop to review the updated config? (Y/n)") + if answer.lower().startswith("y"): + return else: utils.error("You might encounter unexpected errors ! " "Make sur to update your config before opening an issue!") diff --git a/tests.py b/tests.py index d3e2579..6f8fdf6 100644 --- a/tests.py +++ b/tests.py @@ -18,7 +18,6 @@ try: except ImportError: from mock import patch -from modoboa_installer import utils import run @@ -63,7 +62,8 @@ class ConfigFileTestCase(unittest.TestCase): self.assertEqual(config.get("certificate", "type"), "self-signed") self.assertEqual(config.get("database", "engine"), "postgres") - def test_updating_configfile(self): + @patch("modoboa_installer.utils.user_input") + def test_updating_configfile(self, mock_user_input): """Check configfile update mechanism.""" cfgfile_temp = os.path.join(self.workdir, "installer_old.cfg") @@ -82,11 +82,18 @@ class ConfigFileTestCase(unittest.TestCase): [dummy] weird_old_option = "hey """) + mock_user_input.side_effect = ["y"] out = StringIO() sys.stdout = out - utils.update_config(cfgfile_temp) + run.main([ + "--stop-after-configfile-check", + "--configfile", cfgfile_temp, + "example.test"]) self.assertIn("dummy", out.getvalue()) self.assertTrue(Path(self.workdir).glob("*.old")) + self.assertIn("Update complete", + out.getvalue() + ) @patch("modoboa_installer.utils.user_input") def test_interactive_mode_letsencrypt(self, mock_user_input):