Merge pull request #261 from modoboa/feature/upgrade_mode
Installer upgrade mode.
This commit is contained in:
18
README.rst
18
README.rst
@@ -42,6 +42,8 @@ The following components are installed by the installer:
|
|||||||
* Dovecot
|
* Dovecot
|
||||||
* Amavis (with SpamAssassin and ClamAV)
|
* Amavis (with SpamAssassin and ClamAV)
|
||||||
* automx (autoconfiguration service)
|
* automx (autoconfiguration service)
|
||||||
|
* OpenDKIM
|
||||||
|
* Radicale (CalDAV and CardDAV server)
|
||||||
|
|
||||||
If you want to customize configuration before running the installer,
|
If you want to customize configuration before running the installer,
|
||||||
run the following command::
|
run the following command::
|
||||||
@@ -66,6 +68,22 @@ a previous one using the ``--version`` option::
|
|||||||
If you want more information about the installation process, add the
|
If you want more information about the installation process, add the
|
||||||
``--debug`` option to your command line.
|
``--debug`` option to your command line.
|
||||||
|
|
||||||
|
Upgrade mode
|
||||||
|
------------
|
||||||
|
|
||||||
|
An experimental upgrade mode is available.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
You must keep the original configuration file, ie the one used for
|
||||||
|
the installation. Otherwise, you won't be able to use this mode.
|
||||||
|
|
||||||
|
You can activate it as follows::
|
||||||
|
|
||||||
|
$ sudo ./run.py --upgrade <your domain>
|
||||||
|
|
||||||
|
It will automatically install latest versions of modoboa and its plugins.
|
||||||
|
|
||||||
Change the generated hostname
|
Change the generated hostname
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import sys
|
|||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
def install(appname, config):
|
def install(appname, config, upgrade):
|
||||||
"""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")):
|
||||||
@@ -19,7 +19,7 @@ def install(appname, config):
|
|||||||
print("Unknown application {}".format(appname))
|
print("Unknown application {}".format(appname))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
getattr(script, appname.capitalize())(config).run()
|
getattr(script, appname.capitalize())(config, upgrade).run()
|
||||||
except utils.FatalError as inst:
|
except utils.FatalError as inst:
|
||||||
utils.printcolor(u"{}".format(inst), utils.RED)
|
utils.printcolor(u"{}".format(inst), utils.RED)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -85,5 +85,5 @@ class Amavis(base.Installer):
|
|||||||
"""Additional tasks."""
|
"""Additional tasks."""
|
||||||
with open("/etc/mailname", "w") as fp:
|
with open("/etc/mailname", "w") as fp:
|
||||||
fp.write("{}\n".format(self.config.get("general", "hostname")))
|
fp.write("{}\n".format(self.config.get("general", "hostname")))
|
||||||
install("spamassassin", self.config)
|
install("spamassassin", self.config, self.upgrade)
|
||||||
install("clamav", self.config)
|
install("clamav", self.config, self.upgrade)
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ class Automx(base.Installer):
|
|||||||
}
|
}
|
||||||
with_user = True
|
with_user = True
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Get configuration."""
|
"""Get configuration."""
|
||||||
super(Automx, self).__init__(config)
|
super(Automx, self).__init__(*args, **kwargs)
|
||||||
self.venv_path = config.get("automx", "venv_path")
|
self.venv_path = self.config.get("automx", "venv_path")
|
||||||
self.instance_path = config.get("automx", "instance_path")
|
self.instance_path = self.config.get("automx", "instance_path")
|
||||||
|
|
||||||
def get_template_context(self):
|
def get_template_context(self):
|
||||||
"""Additional variables."""
|
"""Additional variables."""
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ class Installer(object):
|
|||||||
with_db = False
|
with_db = False
|
||||||
config_files = []
|
config_files = []
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config, upgrade):
|
||||||
"""Get configuration."""
|
"""Get configuration."""
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.upgrade = upgrade
|
||||||
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")
|
||||||
@@ -67,8 +68,8 @@ class Installer(object):
|
|||||||
self.backend.load_sql_file(
|
self.backend.load_sql_file(
|
||||||
self.dbname, self.dbuser, self.dbpasswd, schema)
|
self.dbname, self.dbuser, self.dbpasswd, schema)
|
||||||
|
|
||||||
def create_user(self):
|
def setup_user(self):
|
||||||
"""Create a system user."""
|
"""Setup a system user."""
|
||||||
if not self.with_user:
|
if not self.with_user:
|
||||||
return
|
return
|
||||||
self.user = self.config.get(self.appname, "user")
|
self.user = self.config.get(self.appname, "user")
|
||||||
@@ -143,7 +144,8 @@ class Installer(object):
|
|||||||
def run(self):
|
def run(self):
|
||||||
"""Run the installer."""
|
"""Run the installer."""
|
||||||
self.install_packages()
|
self.install_packages()
|
||||||
self.create_user()
|
self.setup_user()
|
||||||
|
if not self.upgrade:
|
||||||
self.setup_database()
|
self.setup_database()
|
||||||
self.install_config_files()
|
self.install_config_files()
|
||||||
self.post_run()
|
self.post_run()
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ class Modoboa(base.Installer):
|
|||||||
with_db = True
|
with_db = True
|
||||||
with_user = True
|
with_user = True
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Get configuration."""
|
"""Get configuration."""
|
||||||
super(Modoboa, self).__init__(config)
|
super(Modoboa, self).__init__(*args, **kwargs)
|
||||||
self.venv_path = config.get("modoboa", "venv_path")
|
self.venv_path = self.config.get("modoboa", "venv_path")
|
||||||
self.instance_path = config.get("modoboa", "instance_path")
|
self.instance_path = self.config.get("modoboa", "instance_path")
|
||||||
self.extensions = config.get("modoboa", "extensions").split()
|
self.extensions = self.config.get("modoboa", "extensions").split()
|
||||||
self.devmode = config.getboolean("modoboa", "devmode")
|
self.devmode = self.config.getboolean("modoboa", "devmode")
|
||||||
# Sanity check for amavis
|
# Sanity check for amavis
|
||||||
self.amavis_enabled = False
|
self.amavis_enabled = False
|
||||||
if "modoboa-amavis" in self.extensions:
|
if "modoboa-amavis" in self.extensions:
|
||||||
@@ -87,7 +87,8 @@ class Modoboa(base.Installer):
|
|||||||
packages.append(extension)
|
packages.append(extension)
|
||||||
# Temp fix for https://github.com/modoboa/modoboa-installer/issues/197
|
# Temp fix for https://github.com/modoboa/modoboa-installer/issues/197
|
||||||
python.install_package(
|
python.install_package(
|
||||||
modoboa_package, self.venv_path, binary=False, sudo_user=self.user)
|
modoboa_package, self.venv_path,
|
||||||
|
upgrade=self.upgrade, binary=False, sudo_user=self.user)
|
||||||
if self.dbengine == "postgres":
|
if self.dbengine == "postgres":
|
||||||
packages.append("psycopg2-binary")
|
packages.append("psycopg2-binary")
|
||||||
else:
|
else:
|
||||||
@@ -99,18 +100,23 @@ class Modoboa(base.Installer):
|
|||||||
# Temp. fix
|
# Temp. fix
|
||||||
packages += [
|
packages += [
|
||||||
"https://github.com/modoboa/caldav/tarball/master#egg=caldav"]
|
"https://github.com/modoboa/caldav/tarball/master#egg=caldav"]
|
||||||
python.install_packages(packages, self.venv_path, sudo_user=self.user)
|
python.install_packages(
|
||||||
|
packages, self.venv_path, upgrade=self.upgrade, sudo_user=self.user)
|
||||||
if self.devmode:
|
if self.devmode:
|
||||||
# FIXME: use dev-requirements instead
|
# FIXME: use dev-requirements instead
|
||||||
python.install_packages(
|
python.install_packages(
|
||||||
["django-bower", "django-debug-toolbar"], self.venv_path,
|
["django-bower", "django-debug-toolbar"], self.venv_path,
|
||||||
sudo_user=self.user)
|
upgrade=self.upgrade, sudo_user=self.user)
|
||||||
|
|
||||||
def _deploy_instance(self):
|
def _deploy_instance(self):
|
||||||
"""Deploy Modoboa."""
|
"""Deploy Modoboa."""
|
||||||
target = os.path.join(self.home_dir, "instance")
|
target = os.path.join(self.home_dir, "instance")
|
||||||
if os.path.exists(target):
|
if os.path.exists(target):
|
||||||
if not self.config.getboolean("general", "force"):
|
condition = (
|
||||||
|
not self.upgrade and
|
||||||
|
not self.config.getboolean("general", "force")
|
||||||
|
)
|
||||||
|
if condition:
|
||||||
utils.printcolor(
|
utils.printcolor(
|
||||||
"Target directory for Modoboa deployment ({}) already "
|
"Target directory for Modoboa deployment ({}) already "
|
||||||
"exists. If you choose to continue, it will be removed."
|
"exists. If you choose to continue, it will be removed."
|
||||||
@@ -239,4 +245,5 @@ class Modoboa(base.Installer):
|
|||||||
"""Additional tasks."""
|
"""Additional tasks."""
|
||||||
self._setup_venv()
|
self._setup_venv()
|
||||||
self._deploy_instance()
|
self._deploy_instance()
|
||||||
|
if not self.upgrade:
|
||||||
self.apply_settings()
|
self.apply_settings()
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class Nginx(base.Installer):
|
|||||||
context.update({
|
context.update({
|
||||||
"app_instance_path": (
|
"app_instance_path": (
|
||||||
self.config.get(app, "instance_path")),
|
self.config.get(app, "instance_path")),
|
||||||
"uwsgi_socket_path": Uwsgi(self.config).get_socket_path(app)
|
"uwsgi_socket_path": (
|
||||||
|
Uwsgi(self.config, self.upgrade).get_socket_path(app))
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@@ -97,4 +97,4 @@ class Postfix(base.Installer):
|
|||||||
utils.exec_cmd("postalias {}".format(aliases_file))
|
utils.exec_cmd("postalias {}".format(aliases_file))
|
||||||
|
|
||||||
# Postwhite
|
# Postwhite
|
||||||
install("postwhite", self.config)
|
install("postwhite", self.config, self.upgrade)
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ class Radicale(base.Installer):
|
|||||||
}
|
}
|
||||||
with_user = True
|
with_user = True
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Get configuration."""
|
"""Get configuration."""
|
||||||
super(Radicale, self).__init__(config)
|
super(Radicale, self).__init__(*args, **kwargs)
|
||||||
self.venv_path = config.get("radicale", "venv_path")
|
self.venv_path = self.config.get("radicale", "venv_path")
|
||||||
|
|
||||||
def _setup_venv(self):
|
def _setup_venv(self):
|
||||||
"""Prepare a dedicated virtualenv."""
|
"""Prepare a dedicated virtualenv."""
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Spamassassin(base.Installer):
|
|||||||
"pyzor --homedir {} discover".format(pw[5]),
|
"pyzor --homedir {} discover".format(pw[5]),
|
||||||
sudo_user=amavis_user, login=False
|
sudo_user=amavis_user, login=False
|
||||||
)
|
)
|
||||||
install("razor", self.config)
|
install("razor", self.config, self.upgrade)
|
||||||
if utils.dist_name() in ["debian", "ubuntu"]:
|
if utils.dist_name() in ["debian", "ubuntu"]:
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"perl -pi -e 's/^CRON=0/CRON=1/' /etc/cron.daily/spamassassin")
|
"perl -pi -e 's/^CRON=0/CRON=1/' /etc/cron.daily/spamassassin")
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class SelfSignedCertificate(CertificateBackend):
|
|||||||
return
|
return
|
||||||
raise RuntimeError("Cannot find a directory to store certificate")
|
raise RuntimeError("Cannot find a directory to store certificate")
|
||||||
|
|
||||||
def create(self):
|
def generate_cert(self):
|
||||||
"""Create a certificate."""
|
"""Create a certificate."""
|
||||||
if not self.overwrite_existing_certificate():
|
if not self.overwrite_existing_certificate():
|
||||||
return
|
return
|
||||||
@@ -61,26 +61,30 @@ class SelfSignedCertificate(CertificateBackend):
|
|||||||
class LetsEncryptCertificate(CertificateBackend):
|
class LetsEncryptCertificate(CertificateBackend):
|
||||||
"""Create a certificate using letsencrypt."""
|
"""Create a certificate using letsencrypt."""
|
||||||
|
|
||||||
def create(self):
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Update config."""
|
||||||
|
super(LetsEncryptCertificate, self).__init__(*args, **kwargs)
|
||||||
|
self.hostname = self.config.get("general", "hostname")
|
||||||
|
self.config.set("general", "tls_cert_file", (
|
||||||
|
"/etc/letsencrypt/live/{}/fullchain.pem".format(self.hostname)))
|
||||||
|
self.config.set("general", "tls_key_file", (
|
||||||
|
"/etc/letsencrypt/live/{}/privkey.pem".format(self.hostname)))
|
||||||
|
|
||||||
|
def generate_cert(self):
|
||||||
"""Create a certificate."""
|
"""Create a certificate."""
|
||||||
utils.printcolor(
|
utils.printcolor(
|
||||||
"Generating new certificate using letsencrypt", utils.YELLOW)
|
"Generating new certificate using letsencrypt", utils.YELLOW)
|
||||||
hostname = self.config.get("general", "hostname")
|
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"wget https://dl.eff.org/certbot-auto; chmod a+x certbot-auto",
|
"wget https://dl.eff.org/certbot-auto; chmod a+x certbot-auto",
|
||||||
cwd="/opt")
|
cwd="/opt")
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"/opt/certbot-auto certonly -n --standalone -d {} "
|
"/opt/certbot-auto certonly -n --standalone -d {} "
|
||||||
"-m {} --agree-tos".format(
|
"-m {} --agree-tos".format(
|
||||||
hostname, self.config.get("letsencrypt", "email")))
|
self.hostname, self.config.get("letsencrypt", "email")))
|
||||||
self.config.set("general", "tls_cert_file", (
|
|
||||||
"/etc/letsencrypt/live/{}/fullchain.pem".format(hostname)))
|
|
||||||
self.config.set("general", "tls_key_file", (
|
|
||||||
"/etc/letsencrypt/live/{}/privkey.pem".format(hostname)))
|
|
||||||
with open("/etc/cron.d/letsencrypt", "w") as fp:
|
with open("/etc/cron.d/letsencrypt", "w") as fp:
|
||||||
fp.write("0 */12 * * * root /opt/certbot-auto renew "
|
fp.write("0 */12 * * * root /opt/certbot-auto renew "
|
||||||
"--quiet --no-self-upgrade --force-renewal\n")
|
"--quiet --no-self-upgrade --force-renewal\n")
|
||||||
cfg_file = "/etc/letsencrypt/renewal/{}.conf".format(hostname)
|
cfg_file = "/etc/letsencrypt/renewal/{}.conf".format(self.hostname)
|
||||||
pattern = "s/authenticator = standalone/authenticator = nginx/"
|
pattern = "s/authenticator = standalone/authenticator = nginx/"
|
||||||
utils.exec_cmd("perl -pi -e '{}' {}".format(pattern, cfg_file))
|
utils.exec_cmd("perl -pi -e '{}' {}".format(pattern, cfg_file))
|
||||||
|
|
||||||
|
|||||||
@@ -145,10 +145,15 @@ def copy_from_template(template, dest, context):
|
|||||||
fp.write(ConfigFileTemplate(buf).substitute(context))
|
fp.write(ConfigFileTemplate(buf).substitute(context))
|
||||||
|
|
||||||
|
|
||||||
def check_config_file(dest, interactive=False):
|
def check_config_file(dest, interactive=False, upgrade=False):
|
||||||
"""Create a new installer config file if needed."""
|
"""Create a new installer config file if needed."""
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
return
|
return
|
||||||
|
if upgrade:
|
||||||
|
printcolor(
|
||||||
|
"You cannot upgrade an existing installation without a "
|
||||||
|
"configuration file.", RED)
|
||||||
|
sys.exit(1)
|
||||||
printcolor(
|
printcolor(
|
||||||
"Configuration file {} not found, creating new one."
|
"Configuration file {} not found, creating new one."
|
||||||
.format(dest), YELLOW)
|
.format(dest), YELLOW)
|
||||||
|
|||||||
77
run.py
77
run.py
@@ -17,6 +17,34 @@ from modoboa_installer import system
|
|||||||
from modoboa_installer import utils
|
from modoboa_installer import utils
|
||||||
|
|
||||||
|
|
||||||
|
def installation_disclaimer(args, config):
|
||||||
|
"""Display installation disclaimer."""
|
||||||
|
hostname = config.get("general", "hostname")
|
||||||
|
utils.printcolor(
|
||||||
|
"Warning:\n"
|
||||||
|
"Before you start the installation, please make sure the following "
|
||||||
|
"DNS records exist for domain '{}':\n"
|
||||||
|
" {} IN A <IP ADDRESS OF YOUR SERVER>\n"
|
||||||
|
" IN MX {}.\n".format(
|
||||||
|
args.domain,
|
||||||
|
hostname.replace(".{}".format(args.domain), ""),
|
||||||
|
hostname
|
||||||
|
),
|
||||||
|
utils.CYAN
|
||||||
|
)
|
||||||
|
utils.printcolor(
|
||||||
|
"Your mail server will be installed with the following components:",
|
||||||
|
utils.BLUE)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_disclaimer(config):
|
||||||
|
"""Display upgrade disclaimer."""
|
||||||
|
utils.printcolor(
|
||||||
|
"Your mail server is about to be upgraded and the following components"
|
||||||
|
" will be impacted:", utils.BLUE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(input_args):
|
def main(input_args):
|
||||||
"""Install process."""
|
"""Install process."""
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -38,6 +66,9 @@ def main(input_args):
|
|||||||
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")
|
||||||
|
parser.add_argument(
|
||||||
|
"--upgrade", action="store_true", default=False,
|
||||||
|
help="Run the installer in upgrade mode")
|
||||||
parser.add_argument("domain", type=str,
|
parser.add_argument("domain", type=str,
|
||||||
help="The main domain of your future mail server")
|
help="The main domain of your future mail server")
|
||||||
args = parser.parse_args(input_args)
|
args = parser.parse_args(input_args)
|
||||||
@@ -45,7 +76,7 @@ def main(input_args):
|
|||||||
if args.debug:
|
if args.debug:
|
||||||
utils.ENV["debug"] = True
|
utils.ENV["debug"] = True
|
||||||
utils.printcolor("Welcome to Modoboa installer!\n", utils.GREEN)
|
utils.printcolor("Welcome to Modoboa installer!\n", utils.GREEN)
|
||||||
utils.check_config_file(args.configfile, args.interactive)
|
utils.check_config_file(args.configfile, args.interactive, args.upgrade)
|
||||||
if args.stop_after_configfile_check:
|
if args.stop_after_configfile_check:
|
||||||
return
|
return
|
||||||
config = configparser.SafeConfigParser()
|
config = configparser.SafeConfigParser()
|
||||||
@@ -56,22 +87,12 @@ def main(input_args):
|
|||||||
config.set("general", "domain", args.domain)
|
config.set("general", "domain", args.domain)
|
||||||
config.set("dovecot", "domain", args.domain)
|
config.set("dovecot", "domain", args.domain)
|
||||||
config.set("modoboa", "version", args.version)
|
config.set("modoboa", "version", args.version)
|
||||||
hostname = config.get("general", "hostname")
|
# Display disclaimer
|
||||||
utils.printcolor(
|
if not args.upgrade:
|
||||||
"Warning:\n"
|
installation_disclaimer(args, config)
|
||||||
"Before you start the installation, please make sure the following "
|
else:
|
||||||
"DNS records exist for domain '{}':\n"
|
upgrade_disclaimer(config)
|
||||||
" {} IN A <IP ADDRESS OF YOUR SERVER>\n"
|
# Show concerned components
|
||||||
" IN MX {}.\n".format(
|
|
||||||
args.domain,
|
|
||||||
hostname.replace(".{}".format(args.domain), ""),
|
|
||||||
hostname
|
|
||||||
),
|
|
||||||
utils.CYAN
|
|
||||||
)
|
|
||||||
utils.printcolor(
|
|
||||||
"Your mail server will be installed with the following components:",
|
|
||||||
utils.BLUE)
|
|
||||||
components = []
|
components = []
|
||||||
for section in config.sections():
|
for section in config.sections():
|
||||||
if section in ["general", "database", "mysql", "postgres",
|
if section in ["general", "database", "mysql", "postgres",
|
||||||
@@ -93,17 +114,17 @@ def main(input_args):
|
|||||||
utils.printcolor("Starting...", utils.GREEN)
|
utils.printcolor("Starting...", utils.GREEN)
|
||||||
package.backend.install_many(["sudo", "wget"])
|
package.backend.install_many(["sudo", "wget"])
|
||||||
ssl_backend = ssl.get_backend(config)
|
ssl_backend = ssl.get_backend(config)
|
||||||
if ssl_backend:
|
if ssl_backend and not args.upgrade:
|
||||||
ssl_backend.create()
|
ssl_backend.generate_cert()
|
||||||
scripts.install("amavis", config)
|
scripts.install("amavis", config, args.upgrade)
|
||||||
scripts.install("modoboa", config)
|
scripts.install("modoboa", config, args.upgrade)
|
||||||
scripts.install("automx", config)
|
scripts.install("automx", config, args.upgrade)
|
||||||
scripts.install("radicale", config)
|
scripts.install("radicale", config, args.upgrade)
|
||||||
scripts.install("uwsgi", config)
|
scripts.install("uwsgi", config, args.upgrade)
|
||||||
scripts.install("nginx", config)
|
scripts.install("nginx", config, args.upgrade)
|
||||||
scripts.install("opendkim", config)
|
scripts.install("opendkim", config, args.upgrade)
|
||||||
scripts.install("postfix", config)
|
scripts.install("postfix", config, args.upgrade)
|
||||||
scripts.install("dovecot", config)
|
scripts.install("dovecot", config, args.upgrade)
|
||||||
system.restart_service("cron")
|
system.restart_service("cron")
|
||||||
utils.printcolor(
|
utils.printcolor(
|
||||||
"Congratulations! You can enjoy Modoboa at https://{} (admin:password)"
|
"Congratulations! You can enjoy Modoboa at https://{} (admin:password)"
|
||||||
|
|||||||
41
tests.py
41
tests.py
@@ -30,6 +30,8 @@ class ConfigFileTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def test_configfile_generation(self):
|
def test_configfile_generation(self):
|
||||||
"""Check simple case."""
|
"""Check simple case."""
|
||||||
|
out = StringIO()
|
||||||
|
sys.stdout = out
|
||||||
run.main([
|
run.main([
|
||||||
"--stop-after-configfile-check",
|
"--stop-after-configfile-check",
|
||||||
"--configfile", self.cfgfile,
|
"--configfile", self.cfgfile,
|
||||||
@@ -91,6 +93,45 @@ class ConfigFileTestCase(unittest.TestCase):
|
|||||||
out.getvalue()
|
out.getvalue()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch("modoboa_installer.utils.user_input")
|
||||||
|
def test_upgrade_mode(self, mock_user_input):
|
||||||
|
"""Test upgrade mode launch."""
|
||||||
|
mock_user_input.side_effect = ["no"]
|
||||||
|
# 1. Generate a config file
|
||||||
|
with open(os.devnull, "w") as fp:
|
||||||
|
sys.stdout = fp
|
||||||
|
run.main([
|
||||||
|
"--stop-after-configfile-check",
|
||||||
|
"--configfile", self.cfgfile,
|
||||||
|
"example.test"])
|
||||||
|
# 2. Run upgrade
|
||||||
|
out = StringIO()
|
||||||
|
sys.stdout = out
|
||||||
|
run.main([
|
||||||
|
"--configfile", self.cfgfile,
|
||||||
|
"--upgrade",
|
||||||
|
"example.test"])
|
||||||
|
self.assertIn(
|
||||||
|
"Your mail server is about to be upgraded and the following "
|
||||||
|
"components will be impacted:",
|
||||||
|
out.getvalue()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_upgrade_no_config_file(self):
|
||||||
|
"""Check config file existence check."""
|
||||||
|
out = StringIO()
|
||||||
|
sys.stdout = out
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
run.main([
|
||||||
|
"--configfile", self.cfgfile,
|
||||||
|
"--upgrade",
|
||||||
|
"example.test"
|
||||||
|
])
|
||||||
|
self.assertIn(
|
||||||
|
"You cannot upgrade an existing installation without a "
|
||||||
|
"configuration file.", out.getvalue()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user