[FIX] runbot: use a custom template to create db

Since cb05f2b9d8, when creating a database, the template1 is used, this
allows to customize the template and install some needed Postgress
extensions.

Unfortunately, it's also a source of build failures. For example, if the
pg_activity util is used on a runbot host, database creation may fail
with a message like this:

source database "template1" is being accessed by other users

It's because pg_activity needs a database and uses template1.

With this commit, template1 is still used by default but can be changed
with a system parameter. That way, a custom template can be created on
runbot hosts and used when creating DB in builds.
This commit is contained in:
Christophe Monniez 2020-02-10 16:45:34 +01:00 committed by XavierDo
parent c816ba2161
commit 3e7d98a6b8
4 changed files with 26 additions and 3 deletions

View File

@ -17,6 +17,7 @@ from odoo.exceptions import UserError, ValidationError
from odoo.http import request from odoo.http import request
from odoo.tools import appdirs from odoo.tools import appdirs
from collections import defaultdict from collections import defaultdict
from psycopg2 import sql
from subprocess import CalledProcessError from subprocess import CalledProcessError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -863,10 +864,12 @@ class runbot_build(models.Model):
subprocess.call(cmd) subprocess.call(cmd)
def _local_pg_createdb(self, dbname): def _local_pg_createdb(self, dbname):
icp = self.env['ir.config_parameter']
db_template = icp.get_param('runbot.runbot_db_template', default='template1')
self._local_pg_dropdb(dbname) self._local_pg_dropdb(dbname)
_logger.debug("createdb %s", dbname) _logger.debug("createdb %s", dbname)
with local_pgadmin_cursor() as local_cr: with local_pgadmin_cursor() as local_cr:
local_cr.execute("""CREATE DATABASE "%s" TEMPLATE template1 LC_COLLATE 'C' ENCODING 'unicode'""" % dbname) local_cr.execute(sql.SQL("""CREATE DATABASE {} TEMPLATE %s LC_COLLATE 'C' ENCODING 'unicode'""").format(sql.Identifier(dbname)), (db_template,))
def _log(self, func, message, level='INFO', log_type='runbot', path='runbot'): def _log(self, func, message, level='INFO', log_type='runbot', path='runbot'):
self.ensure_one() self.ensure_one()

View File

@ -47,6 +47,18 @@ class RunboHost(models.Model):
values['disp_name'] = values['name'] values['disp_name'] = values['name']
return super().create(values) return super().create(values)
def _bootstrap_db_template(self):
""" boostrap template database if needed """
icp = self.env['ir.config_parameter']
db_template = icp.get_param('runbot.runbot_db_template', default='template1')
if db_template and db_template != 'template1':
with local_pgadmin_cursor() as local_cr:
local_cr.execute("""SELECT datname FROM pg_catalog.pg_database WHERE datname = '%s';""" % db_template)
res = local_cr.fetchone()
if not res:
local_cr.execute("""CREATE DATABASE "%s" TEMPLATE template1 LC_COLLATE 'C' ENCODING 'unicode'""" % db_template)
# TODO UPDATE pg_database set datallowconn = false, datistemplate = true (but not enough privileges)
def _bootstrap(self): def _bootstrap(self):
""" Create needed directories in static """ """ Create needed directories in static """
dirs = ['build', 'nginx', 'repo', 'sources', 'src', 'docker'] dirs = ['build', 'nginx', 'repo', 'sources', 'src', 'docker']
@ -54,6 +66,7 @@ class RunboHost(models.Model):
static_dirs = {d: os.path.join(static_path, d) for d in dirs} static_dirs = {d: os.path.join(static_path, d) for d in dirs}
for dir, path in static_dirs.items(): for dir, path in static_dirs.items():
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
self._bootstrap_db_template()
def _docker_build(self): def _docker_build(self):
""" build docker image """ """ build docker image """

View File

@ -15,6 +15,7 @@ class ResConfigSettings(models.TransientModel):
runbot_max_age = fields.Integer('Max branch age (in days)') runbot_max_age = fields.Integer('Max branch age (in days)')
runbot_logdb_uri = fields.Char('Runbot URI for build logs') runbot_logdb_uri = fields.Char('Runbot URI for build logs')
runbot_update_frequency = fields.Integer('Update frequency (in seconds)') runbot_update_frequency = fields.Integer('Update frequency (in seconds)')
runbot_template = fields.Char('Postgresql template', help="Postgresql template to use when creating DB's")
runbot_message = fields.Text('Frontend warning message') runbot_message = fields.Text('Frontend warning message')
@api.model @api.model
@ -29,7 +30,8 @@ class ResConfigSettings(models.TransientModel):
runbot_max_age=int(get_param('runbot.runbot_max_age', default=30)), runbot_max_age=int(get_param('runbot.runbot_max_age', default=30)),
runbot_logdb_uri=get_param('runbot.runbot_logdb_uri', default=False), runbot_logdb_uri=get_param('runbot.runbot_logdb_uri', default=False),
runbot_update_frequency=int(get_param('runbot.runbot_update_frequency', default=10)), runbot_update_frequency=int(get_param('runbot.runbot_update_frequency', default=10)),
runbot_message = get_param('runbot.runbot_message', default=''), runbot_template=get_param('runbot.runbot_db_template'),
runbot_message=get_param('runbot.runbot_message', default=''),
) )
return res return res
@ -44,4 +46,5 @@ class ResConfigSettings(models.TransientModel):
set_param("runbot.runbot_max_age", self.runbot_max_age) set_param("runbot.runbot_max_age", self.runbot_max_age)
set_param("runbot.runbot_logdb_uri", self.runbot_logdb_uri) set_param("runbot.runbot_logdb_uri", self.runbot_logdb_uri)
set_param('runbot.runbot_update_frequency', self.runbot_update_frequency) set_param('runbot.runbot_update_frequency', self.runbot_update_frequency)
set_param('runbot.runbot_db_template', self.runbot_template)
set_param('runbot.runbot_message', self.runbot_message) set_param('runbot.runbot_message', self.runbot_message)

View File

@ -50,9 +50,13 @@
<label for="runbot_update_frequency" class="col-xs-3 o_light_label" style="width: 60%;"/> <label for="runbot_update_frequency" class="col-xs-3 o_light_label" style="width: 60%;"/>
<field name="runbot_update_frequency" style="width: 30%;"/> <field name="runbot_update_frequency" style="width: 30%;"/>
</div> </div>
<div class="mt-16 row">
<label for="runbot_template" class="col-xs-3 o_light_label" style="width: 60%;"/>
<field name="runbot_template" style="width: 30%;"/>
</div>
<div class="mt-16 row"> <div class="mt-16 row">
<label for="runbot_message" class="col-xs-3 o_light_label" style="width: 60%;"/> <label for="runbot_message" class="col-xs-3 o_light_label" style="width: 60%;"/>
<field name="runbot_message" style="width: 30%;"/> <field name="runbot_message" style="width: 100%;"/>
</div> </div>
</div> </div>
</div> </div>