From dc688ecc39d1e0f31b96e1fbd152fb3b4b2130b6 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 00:14:56 +0200 Subject: [PATCH 1/5] fixed the path for core/commands/templates --- MANIFEST.in | 3 ++- modoboa/core/commands/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 38f115085..02a707cf3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include README.rst COPYING requirements.txt recursive-include modoboa *.html *.js *.css *.png *.po *.mo *.json *.gif recursive-include modoboa/core/templates *.tpl -recursive-include doc * \ No newline at end of file +recursive-include modoboa/core/commands/templates *.tpl +recursive-include doc * diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index e7ef1ae9e..9b2b1c9f2 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -19,7 +19,7 @@ class Command(object): self._verbose = verbose if not settings.configured: settings.configure() - self._templates_dir = "%s/commands/templates" % os.path.dirname(__file__) + self._templates_dir = "%s/templates" % os.path.dirname(__file__) def _render_template(self, tplfile, env): fp = open(tplfile) From d8f39f700b00200a171c244c540a494c21be5499 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 01:05:00 +0200 Subject: [PATCH 2/5] added possibility to supply database settings and domain via command line args --- modoboa/core/commands/deploy.py | 100 +++++++++++++++++++++++--------- requirements.txt | 1 + 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 91c90341e..322decb85 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -4,6 +4,8 @@ import os import shutil import getpass import subprocess +import dj_database_url + from django.core import management from django.template import Context, Template from modoboa.lib.sysutils import exec_cmd @@ -11,12 +13,12 @@ from . import Command dbconn_tpl = """ '{{ conn_name }}': { - 'ENGINE': 'django.db.backends.{{ dbtype }}', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '{{ dbname }}', # Or path to database file if using sqlite3. - 'USER': '{{ username }}', # Not used with sqlite3. - 'PASSWORD': '{{ password }}', # Not used with sqlite3. - 'HOST': '{{ dbhost }}', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'mysql' %} + 'ENGINE': '{{ ENGINE }}', + 'NAME': '{{ NAME }}', # Or path to database file if using sqlite3. + 'USER': '{% if USER %}{{ USER }}{% endif %}', # Not used with sqlite3. + 'PASSWORD': '{% if PASSWORD %}{{ PASSWORD }}{% endif %}', # Not used with sqlite3. + 'HOST': '{% if HOST %}{{ HOST }}{% endif %}', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '{% if PORT %}{{ PORT }}{% endif %}', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'django.db.backends.mysql' %} 'OPTIONS' : { "init_command" : 'SET foreign_key_checks = 0;', },{% endif %} @@ -43,6 +45,16 @@ class DeployCommand(Command): '--collectstatic', action='store_true', default=False, help='Run django collectstatic command' ) + self._parser.add_argument( + '--dburl', type=str, nargs=1, default=None, + help='The database-url for your modoboa instance') + self._parser.add_argument( + '--amavis_dburl', type=str, nargs=1, default=None, + help='The database-url for your amavis instance') + self._parser.add_argument( + '--domain', type=str, nargs=1, default=None, + help='The domain under which you want to deploy modoboa') + def _exec_django_command(self, name, cwd, *args): """Run a django command for the freshly created project @@ -76,22 +88,28 @@ class DeployCommand(Command): """ print "Configuring database connection: %s" % name info = {'conn_name': name, - 'dbtype': raw_input('Database type (mysql, postgres or sqlite3): ')} - if info['dbtype'] not in ['mysql', 'postgres', 'sqlite3']: - info['dbtype'] = 'mysql' - if info['dbtype'] == 'postgres': - info['dbtype'] = 'postgresql_psycopg2' - if info['dbtype'] == 'sqlite3': - info['dbname'] = '%s.db' % name + 'ENGINE': raw_input('Database type (mysql, postgres or sqlite3): ')} + if info['ENGINE'] not in ['mysql', 'postgres', 'sqlite3']: + info['ENGINE'] = 'django.db.backends.mysql' + default_port = 3306 + if info['ENGINE'] == 'postgres': + info['ENGINE'] = 'django.db.backends.postgresql_psycopg2' + default_port = 5432 + if info['ENGINE'] == 'sqlite3': + info['ENGINE'] = 'django.db.backends.sqlite3' + info['NAME'] = '%s.db' % name else: - default_host = 'localhost' if info['dbtype'] == 'mysql' else '' - info['dbhost'] = \ - raw_input("Database host (default: '%s'): " % default_host) - if info['dbhost'] == '': - info['dbhost'] = default_host - info['dbname'] = raw_input('Database name: ') - info['username'] = raw_input('Username: ') - info['password'] = getpass.getpass('Password: ') + # No need to set HOST to localhost, since django will do this + # automatically if HOST is not supplied + info['HOST'] = raw_input("Database host (default: localhost )") + + info['PORT'] = raw_input("Database port: (default: '%s'): " % default_port) + #leave port setting empty, if default value is supplied and leave it to django + if info['PORT'] == default_port: + info['PORT'] = '' + info['NAME'] = raw_input('Database name: ') + info['USER'] = raw_input('Username: ') + info['PASSWORD'] = getpass.getpass('Password: ') return info def handle(self, parsed_args): @@ -109,13 +127,41 @@ class DeployCommand(Command): django14 = False t = Template(dbconn_tpl) - default_conn = t.render(Context(self.ask_db_info())) - amavis_conn = t.render(Context(self.ask_db_info('amavis'))) \ - if parsed_args.with_amavis else None - allowed_host = raw_input( - 'Under which domain do you want to deploy modoboa? ' - ) + if parsed_args.dburl: + info = dj_database_url.config(default=parsed_args.dburl[0]) + # In case the user fails to supply a valid database url, fallback to manual mode + if not info: + print "There was a problem with your database-url. \n" + info = self.ask_db_info() + #If we set this earlier, our fallback method will never be triggered + info['conn_name'] = 'default' + else: + info = self.ask_db_info() + + default_conn = t.render(Context(info)) + + if parsed_args.with_amavis: + if parsed_args.amavis_dburl: + amavis_info = dj_database_url.config(default=parsed_args.amavis_dburl[0]) + # In case the user fails to supply a valid database url, fallback to manual mode + if not amavis_info: + amavis_info = self.ask_db_info('amavis') + #If we set this earlier, our fallback method will never be triggered + amavis_info['conn_name'] = 'amavis' + else: + amavis_info = self.ask_db_info('amavis') + + amavis_conn = t.render(Context(amavis_info)) + else: + amavis_conn = None + + if parsed_args.domain: + allowed_host = parsed_args.domain[0] + else: + allowed_host = raw_input( + 'Under which domain do you want to deploy modoboa? ' + ) mod = __import__(parsed_args.name, globals(), locals(), ['settings']) tpl = self._render_template( diff --git a/requirements.txt b/requirements.txt index 6b74c0566..d7ba96b5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ django django-reversion +dj-database-url south lxml pycrypto From 999a78bf2e89f6cbd4ef442aaee0fea9af03d2a1 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 01:26:50 +0200 Subject: [PATCH 3/5] added silent installation / database_urls to the docs --- doc/getting_started/install.rst | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/doc/getting_started/install.rst b/doc/getting_started/install.rst index 63dd59bfb..8a5afff43 100644 --- a/doc/getting_started/install.rst +++ b/doc/getting_started/install.rst @@ -94,7 +94,7 @@ Deployment `modoboa-admin.py`, a command line tool, let you deploy a *ready-to-use* Modoboa site using only one instruction:: - $ modoboa-admin.py deploy modoboa_example --syncdb --collectstatic [--with-amavis] + $ modoboa-admin.py deploy modoboa_example --syncdb --collectstatic [--with-amavis] [--dburl database-url] [--amavis_dburl database-url] Just answer the few questions and you're done. You can now go to the :ref:`first_use` section. @@ -104,6 +104,31 @@ Just answer the few questions and you're done. You can now go to the The `--with-amavis` option must be set only if you intend to use the :ref:`amavis_frontend`. + +In case you need a **silent installation**, e.g. if you're using Salt-Stack. +It's possible to supply the database credentials as commandline arguments. + +.. note:: + + `--dburl database-url` for the modoboa database credentials + `--amavis_dburl database-url` for the amavis database credentials + + Your database-url should meet the following syntax: + ``scheme://[user:pass@][host:port]/dbname`` + + **or** + + ``sqlite:////full/path/to/your/database/file.sqlite`` + + Available `schemes` are: + * postgres + * postgresql + * postgis + * mysql + * mysql2 + * sqlite + + .. note:: If you plan to serve Modoboa using a URL prefix, you must change the From 9efd08438d13c0c3eb914aa790606d75580271dd Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Fri, 18 Oct 2013 11:39:26 +0200 Subject: [PATCH 4/5] added preliminary test i am new to this --- tests/install_from_scratch.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/install_from_scratch.py b/tests/install_from_scratch.py index 4d6800cec..4924cf03f 100644 --- a/tests/install_from_scratch.py +++ b/tests/install_from_scratch.py @@ -64,5 +64,38 @@ class DeployTest(unittest.TestCase): self.assertEqual(code, 0) +class SilentDeployTest(unittest.TestCase): + dbtype = "mysql" + dbhost = "localhost" + projname = "modoboa_silenttest" + dbuser = "root" + dbpassword = "toto" + dburl = "%s://%s:%s@%s/%s" % (self.dbtype, self.dbuser, self.dbpassword, self.dbhost, self.projname) + + def setUp(self): + cmd = "mysqladmin -u %s -p%s create %s" % (self.dbuser, self.dbpassword, self.projname) + code, out = runcmd(cmd) + self.assertEqual(code, 0) + self.workdir = tempfile.mkdtemp() + + def tearDown(self): + if hasattr(self, "workdir"): + shutil.rmtree(self.workdir) + child = pexpect.spawn("mysqladmin -u root -p%s drop %s" % (self.dbpassword, self.projname)) + child.expect("\[y/N\]") + child.sendline("y") + child.expect('Database "%s" dropped' % self.projname) + + def test(self): + timeout = 2 + cmd = "modoboa-admin.py deploy --syncdb --collectstatic %s --dburl --domain %s" % (self.projname, self.dburl, 'localhost') + child = pexpect.spawn(cmd, cwd=self.workdir) + fout = open('install_silently_from_scratch.log','w') + child.logfile = fout + child.wait() + fout.close() + self.assertEqual(child.exitstatus, 0) + + if __name__ == "__main__": unittest.main() From 17c198e26f9ce9e2843b108f640f3ae8f06fe2ab Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Sat, 2 Nov 2013 12:54:50 +0100 Subject: [PATCH 5/5] fixed wrong driver for PostgreSQL in dovecot conf --- doc/integration/imap_and_smtp.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/integration/imap_and_smtp.rst b/doc/integration/imap_and_smtp.rst index 0a6e89a24..521371b0c 100644 --- a/doc/integration/imap_and_smtp.rst +++ b/doc/integration/imap_and_smtp.rst @@ -113,7 +113,7 @@ content inside:: # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext args = /etc/dovecot/dovecot-sql.conf.ext } - + userdb sql { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext @@ -151,7 +151,7 @@ PostgreSQL users :: - driver = postgresql + driver = pgsql connect = host= dbname= user= password= @@ -215,7 +215,7 @@ proper permissions:: unix_listener dict { mode = 0600 user = - #group = + #group = } } @@ -394,12 +394,12 @@ Finally, configure the ``sieve`` plugin by editing the :file:`conf.d/90-sieve.conf` file. Put the follwing caontent inside:: plugin { - # Location of the active script. When ManageSieve is used this is actually + # Location of the active script. When ManageSieve is used this is actually # a symlink pointing to the active script in the sieve storage directory. sieve = ~/.dovecot.sieve # - # The path to the directory where the personal Sieve scripts are stored. For + # The path to the directory where the personal Sieve scripts are stored. For # ManageSieve this is where the uploaded scripts are stored. sieve_dir = ~/sieve } @@ -442,12 +442,12 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file virtual_transport = dovecot dovecot_destination_recipient_limit = 1 - virtual_minimum_uid = + virtual_minimum_uid = virtual_gid_maps = static: virtual_uid_maps = static: virtual_mailbox_base = /var/vmail - relay_domains = + relay_domains = virtual_mailbox_domains = :/etc/postfix/sql-domains.cf virtual_alias_domains = :/etc/postfix/sql-domain-aliases.cf virtual_mailbox_maps = :/etc/postfix/sql-mailboxes.cf