Merge pull request #16 from simme--/master

bugfix #490 and added possibility of silent installation using database-urls
This commit is contained in:
Antoine Nguyen
2013-11-02 08:02:02 -07:00
7 changed files with 143 additions and 37 deletions
+2 -1
View File
@@ -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 *
recursive-include modoboa/core/commands/templates *.tpl
recursive-include doc *
+26 -1
View File
@@ -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
+7 -7
View File
@@ -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=<postgres socket> dbname=<database> user=<user> password=<password>
@@ -215,7 +215,7 @@ proper permissions::
unix_listener dict {
mode = 0600
user = <user owning mailboxes>
#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 = <vmail user id>
virtual_minimum_uid = <vmail user id>
virtual_gid_maps = static:<vmail group id>
virtual_uid_maps = static:<vmail user id>
virtual_mailbox_base = /var/vmail
relay_domains =
relay_domains =
virtual_mailbox_domains = <driver>:/etc/postfix/sql-domains.cf
virtual_alias_domains = <driver>:/etc/postfix/sql-domain-aliases.cf
virtual_mailbox_maps = <driver>:/etc/postfix/sql-mailboxes.cf
+1 -1
View File
@@ -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)
+73 -27
View File
@@ -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(
+1
View File
@@ -1,5 +1,6 @@
django
django-reversion
dj-database-url
south
lxml
pycrypto
+33
View File
@@ -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()