Formating, force outdated config check
This commit is contained in:
@@ -54,12 +54,6 @@ run the following command::
|
|||||||
|
|
||||||
$ ./run.py --stop-after-configfile-check <your domain>
|
$ ./run.py --stop-after-configfile-check <your domain>
|
||||||
|
|
||||||
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 <your domain>
|
|
||||||
|
|
||||||
An interactive mode is also available::
|
An interactive mode is also available::
|
||||||
|
|
||||||
$ ./run.py --interactive <your domain>
|
$ ./run.py --interactive <your domain>
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ class Backup:
|
|||||||
path_exists = os.path.exists(path)
|
path_exists = os.path.exists(path)
|
||||||
|
|
||||||
if path_exists and os.path.isfile(path):
|
if path_exists and os.path.isfile(path):
|
||||||
utils.printcolor(
|
utils.error("Error, you provided a file instead of a directory!")
|
||||||
"Error, you provided a file instead of a directory!", utils.RED)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not path_exists:
|
if not path_exists:
|
||||||
@@ -58,9 +57,7 @@ class Backup:
|
|||||||
utils.mkdir_safe(path, stat.S_IRWXU |
|
utils.mkdir_safe(path, stat.S_IRWXU |
|
||||||
stat.S_IRWXG, pw[2], pw[3])
|
stat.S_IRWXG, pw[2], pw[3])
|
||||||
else:
|
else:
|
||||||
utils.printcolor(
|
utils.error("Error, backup directory not present.")
|
||||||
"Error, backup directory not present.", utils.RED
|
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(os.listdir(path)) != 0:
|
if len(os.listdir(path)) != 0:
|
||||||
@@ -80,9 +77,7 @@ class Backup:
|
|||||||
shutil.rmtree(os.path.join(path, "databases"),
|
shutil.rmtree(os.path.join(path, "databases"),
|
||||||
ignore_errors=False)
|
ignore_errors=False)
|
||||||
else:
|
else:
|
||||||
utils.printcolor(
|
utils.error("Error: backup directory not clean.")
|
||||||
"Error: backup directory not clean.", utils.RED
|
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.backup_path = path
|
self.backup_path = path
|
||||||
@@ -131,8 +126,8 @@ class Backup:
|
|||||||
home_path = self.config.get("dovecot", "home_dir")
|
home_path = self.config.get("dovecot", "home_dir")
|
||||||
|
|
||||||
if not os.path.exists(home_path) or os.path.isfile(home_path):
|
if not os.path.exists(home_path) or os.path.isfile(home_path):
|
||||||
utils.printcolor("Error backing up Email, provided path "
|
utils.error("Error backing up Email, provided path "
|
||||||
f" ({home_path}) seems not right...", utils.RED)
|
f" ({home_path}) seems not right...")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dst = os.path.join(self.backup_path, "mails/")
|
dst = os.path.join(self.backup_path, "mails/")
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class Installer(object):
|
|||||||
return
|
return
|
||||||
exitcode, output = package.backend.install_many(packages)
|
exitcode, output = package.backend.install_many(packages)
|
||||||
if exitcode:
|
if exitcode:
|
||||||
utils.printcolor("Failed to install dependencies", utils.RED)
|
utils.error("Failed to install dependencies")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def get_config_files(self):
|
def get_config_files(self):
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ class Restore:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not os.path.isdir(restore):
|
if not os.path.isdir(restore):
|
||||||
utils.printcolor(
|
utils.error(
|
||||||
"Provided path is not a directory !", utils.RED)
|
"Provided path is not a directory !")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
modoba_sql_file = os.path.join(restore, "databases/modoboa.sql")
|
modoba_sql_file = os.path.join(restore, "databases/modoboa.sql")
|
||||||
if not os.path.isfile(modoba_sql_file):
|
if not os.path.isfile(modoba_sql_file):
|
||||||
utils.printcolor(
|
utils.error(
|
||||||
modoba_sql_file + " not found, please check your backup", utils.RED)
|
modoba_sql_file + " not found, please check your backup")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Everything seems alright here, proceeding...
|
# Everything seems alright here, proceeding...
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class LetsEncryptCertificate(CertificateBackend):
|
|||||||
elif "centos" in name:
|
elif "centos" in name:
|
||||||
package.backend.install("certbot")
|
package.backend.install("certbot")
|
||||||
else:
|
else:
|
||||||
utils.printcolor("Failed to install certbot, aborting.", utils.RED)
|
utils.printcolor("Failed to install certbot, aborting.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
# Nginx plugin certbot
|
# Nginx plugin certbot
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ def check_config_file(dest, interactive=False, upgrade=False, backup=False, rest
|
|||||||
"""Create a new installer config file if needed."""
|
"""Create a new installer config file if needed."""
|
||||||
is_present = True
|
is_present = True
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
return is_present
|
return is_present, update_config(dest, False)
|
||||||
if upgrade:
|
if upgrade:
|
||||||
printcolor(
|
printcolor(
|
||||||
"You cannot upgrade an existing installation without a "
|
"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."
|
"Configuration file {} not found, creating new one."
|
||||||
.format(dest), YELLOW)
|
.format(dest), YELLOW)
|
||||||
gen_config(dest, interactive)
|
gen_config(dest, interactive)
|
||||||
return is_present
|
return is_present, None
|
||||||
|
|
||||||
|
|
||||||
def has_colours(stream):
|
def has_colours(stream):
|
||||||
@@ -340,7 +340,7 @@ def load_config_template(interactive):
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def update_config(path):
|
def update_config(path, apply_update=True):
|
||||||
"""Update an existing config file."""
|
"""Update an existing config file."""
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
with open(path) as fp:
|
with open(path) as fp:
|
||||||
@@ -353,27 +353,33 @@ def update_config(path):
|
|||||||
update = False
|
update = False
|
||||||
|
|
||||||
dropped_sections = list(set(old_sections) - set(new_sections))
|
dropped_sections = list(set(old_sections) - set(new_sections))
|
||||||
|
added_sections = list(set(new_sections) - set(old_sections))
|
||||||
if len(dropped_sections) > 0:
|
if len(dropped_sections) > 0 and not apply_update:
|
||||||
printcolor("Following section(s) will not be ported "
|
printcolor("Following section(s) will not be ported "
|
||||||
"due to being deleted or renamed: " +
|
"due to being deleted or renamed: " +
|
||||||
', '.join(dropped_sections),
|
', '.join(dropped_sections),
|
||||||
RED)
|
RED)
|
||||||
|
|
||||||
|
if len(dropped_sections) + len(added_sections) > 0:
|
||||||
|
update = True
|
||||||
|
|
||||||
for section in new_sections:
|
for section in new_sections:
|
||||||
if section in old_sections:
|
if section in old_sections:
|
||||||
new_options = new_config.options(section)
|
new_options = new_config.options(section)
|
||||||
old_options = config.options(section)
|
old_options = config.options(section)
|
||||||
|
|
||||||
dropped_options = list(set(old_options) - set(new_options))
|
dropped_options = list(set(old_options) - set(new_options))
|
||||||
|
added_options = list(set(new_options) - set(old_sections))
|
||||||
if len(dropped_options) > 0:
|
if len(dropped_options) > 0 and not apply_update:
|
||||||
printcolor(f"Following option(s) from section: {section}, "
|
printcolor(f"Following option(s) from section: {section}, "
|
||||||
"will not be ported due to being "
|
"will not be ported due to being "
|
||||||
"deleted or renamed: " +
|
"deleted or renamed: " +
|
||||||
', '.join(dropped_options),
|
', '.join(dropped_options),
|
||||||
RED)
|
RED)
|
||||||
|
if len(dropped_options) + len(added_options) > 0:
|
||||||
|
update = True
|
||||||
|
|
||||||
|
if apply_update:
|
||||||
for option in new_options:
|
for option in new_options:
|
||||||
if option in old_options:
|
if option in old_options:
|
||||||
value = config.get(section, option, raw=True)
|
value = config.get(section, option, raw=True)
|
||||||
@@ -381,7 +387,7 @@ def update_config(path):
|
|||||||
update = True
|
update = True
|
||||||
new_config.set(section, option, value)
|
new_config.set(section, option, value)
|
||||||
|
|
||||||
if update:
|
if update and apply_update:
|
||||||
# Backing up old config file
|
# Backing up old config file
|
||||||
date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
|
date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
|
||||||
dest = f"{os.path.splitext(path)[0]}_{date}.old"
|
dest = f"{os.path.splitext(path)[0]}_{date}.old"
|
||||||
@@ -391,13 +397,19 @@ def update_config(path):
|
|||||||
with open(path, "w") as configfile:
|
with open(path, "w") as configfile:
|
||||||
new_config.write(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_username = getpass.getuser()
|
||||||
current_user = pwd.getpwnam(current_username)
|
current_user = pwd.getpwnam(current_username)
|
||||||
os.chown(dest, current_user[2], current_user[3])
|
os.chown(dest, current_user[2], current_user[3])
|
||||||
os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR)
|
os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR)
|
||||||
|
|
||||||
return dest
|
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
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
39
run.py
39
run.py
@@ -116,6 +116,15 @@ def backup_system(config, args):
|
|||||||
scripts.backup(app, config, backup_path)
|
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):
|
def main(input_args):
|
||||||
"""Install process."""
|
"""Install process."""
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -134,10 +143,6 @@ def main(input_args):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--stop-after-configfile-check", action="store_true", default=False,
|
"--stop-after-configfile-check", action="store_true", default=False,
|
||||||
help="Check configuration, generate it if needed and exit")
|
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(
|
parser.add_argument(
|
||||||
"--interactive", action="store_true", default=False,
|
"--interactive", action="store_true", default=False,
|
||||||
help="Generate configuration file with user interaction")
|
help="Generate configuration file with user interaction")
|
||||||
@@ -184,18 +189,7 @@ def main(input_args):
|
|||||||
|
|
||||||
utils.success("Welcome to Modoboa installer!\n")
|
utils.success("Welcome to Modoboa installer!\n")
|
||||||
|
|
||||||
# Update configfile
|
is_config_file_available, outdate_config = utils.check_config_file(
|
||||||
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)
|
args.configfile, args.interactive, args.upgrade, args.backup, is_restoring)
|
||||||
|
|
||||||
if not is_config_file_available and (
|
if not is_config_file_available and (
|
||||||
@@ -203,6 +197,19 @@ def main(input_args):
|
|||||||
utils.error("No config file found.")
|
utils.error("No config file found.")
|
||||||
return
|
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:
|
if args.stop_after_configfile_check:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user