changeset 8592:307c876a6e89

db: introduce db-create --reuse option Support use of an existing database so the Kallithea database user doesn't have to be granted permissions to create databases. The existing database must of course have been created "correctly", for example using the right charset and collation on MariaDB/MySQL. Creating the database manually also provides a way to avoid the hardcoded defaults.
author Mads Kiilerich <mads@kiilerich.com>
date Thu, 25 Jun 2020 01:51:18 +0200
parents 8759238d7356
children 922808a61274
files docs/setup.rst kallithea/bin/kallithea_cli_db.py kallithea/lib/db_manage.py
diffstat 3 files changed, 25 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/docs/setup.rst	Thu Jun 25 02:06:01 2020 +0200
+++ b/docs/setup.rst	Thu Jun 25 01:51:18 2020 +0200
@@ -84,6 +84,15 @@
 location to its database.  (Note: make sure you specify the correct
 path to the root).
 
+.. note:: It is also possible to use an existing database. For example,
+          when using PostgreSQL without granting general createdb privileges to
+          the PostgreSQL kallithea user, set ``sqlalchemy.url =
+          postgresql://kallithea:password@localhost/kallithea`` and create the
+          database like::
+
+              sudo -u postgres createdb 'kallithea' --owner 'kallithea'
+              kallithea-cli db-create -c my.ini --reuse
+
 Prepare front-end files
 ^^^^^^^^^^^^^^^^^^^^^^^
 
--- a/kallithea/bin/kallithea_cli_db.py	Thu Jun 25 02:06:01 2020 +0200
+++ b/kallithea/bin/kallithea_cli_db.py	Thu Jun 25 01:51:18 2020 +0200
@@ -20,6 +20,8 @@
 
 
 @cli_base.register_command(config_file=True)
+@click.option('--reuse/--no-reuse', default=False,
+        help='Reuse and clean existing database instead of dropping and creating (default: no reuse)')
 @click.option('--user', help='Username of administrator account.')
 @click.option('--password', help='Password for administrator account.')
 @click.option('--email', help='Email address of administrator account.')
@@ -28,7 +30,7 @@
 @click.option('--force-no', is_flag=True, help='Answer no to every question.')
 @click.option('--public-access/--no-public-access', default=True,
         help='Enable/disable public access on this installation (default: enable)')
-def db_create(user, password, email, repos, force_yes, force_no, public_access):
+def db_create(user, password, email, repos, force_yes, force_no, public_access, reuse):
     """Initialize the database.
 
     Create all required tables in the database specified in the configuration
@@ -57,7 +59,7 @@
     )
     dbmanage = DbManage(dbconf=dbconf, root=kallithea.CONFIG['here'],
                         tests=False, cli_args=cli_args)
-    dbmanage.create_tables()
+    dbmanage.create_tables(reuse_database=reuse)
     repo_root_path = dbmanage.prompt_repo_root_path(None)
     dbmanage.create_settings(repo_root_path)
     dbmanage.create_default_user()
--- a/kallithea/lib/db_manage.py	Thu Jun 25 02:06:01 2020 +0200
+++ b/kallithea/lib/db_manage.py	Thu Jun 25 01:51:18 2020 +0200
@@ -72,20 +72,28 @@
             init_model(engine)
             self.sa = Session()
 
-    def create_tables(self):
+    def create_tables(self, reuse_database=False):
         """
         Create database (optional) and tables.
-        The database will be dropped (if it exists) and a new one created.
+        If reuse_database is false, the database will be dropped (if it exists)
+        and a new one created. If true, the existing database will be reused
+        and cleaned for content.
         """
         url = sqlalchemy.engine.url.make_url(self.dburi)
         database = url.database
-        log.info("The existing database %r will be destroyed and created." % database)
+        if reuse_database:
+            log.info("The content of the database %r will be destroyed and new tables created." % database)
+        else:
+            log.info("The existing database %r will be destroyed and a new one created." % database)
+
         if not self.tests:
             if not self._ask_ok('Are you sure to destroy old database? [y/n]'):
                 print('Nothing done.')
                 sys.exit(0)
 
-        if True:
+        if reuse_database:
+            Base.metadata.drop_all()
+        else:
             if url.drivername == 'mysql':
                 url.database = None  # don't connect to the database (it might not exist)
                 engine = sqlalchemy.create_engine(url)