Skip to content

Commit 10dceb9

Browse files
committed
[IMP] runbot: introduce cluster restore
1 parent 172efb6 commit 10dceb9

3 files changed

Lines changed: 52 additions & 37 deletions

File tree

runbot/container.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ def _docker_run(cmd=False, log_path=False, build_dir=False, container_name=False
261261
run_cmd = cmd
262262
run_cmd = f'cd /data/build;touch start-{container_name};{run_cmd};cd /data/build;touch end-{container_name}'
263263
run_cmd_repr = str(run_cmd)
264-
if len(run_cmd_repr) > 250:
265-
run_cmd_repr = run_cmd_repr[:250] + '...'
264+
if len(run_cmd_repr) > 300:
265+
run_cmd_repr = run_cmd_repr[:250] + '...' + run_cmd_repr[-50:]
266266
_logger.info('Docker run command: %s', run_cmd_repr)
267267
docker_clear_state(container_name, build_dir) # ensure that no state are remaining
268268
build_dir = file_path(build_dir)
@@ -364,9 +364,6 @@ def docker_state(container_name, build_dir):
364364
if not exist:
365365
return 'VOID'
366366

367-
if os.path.exists(os.path.join(build_dir, f'end-{container_name}')):
368-
return 'END'
369-
370367
state = 'UNKNOWN'
371368
if started:
372369
docker_client = docker.from_env()
@@ -376,7 +373,6 @@ def docker_state(container_name, build_dir):
376373
state = 'RUNNING' if container.status in ('created', 'running', 'paused') else 'GHOST'
377374
except docker.errors.NotFound:
378375
state = 'GHOST'
379-
# check if the end- file has been written in between time
380376
if state == 'GHOST' and os.path.exists(os.path.join(build_dir, f'end-{container_name}')):
381377
state = 'END'
382378
return state

runbot/models/build.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
from dateutil import parser
1717
from markupsafe import Markup
18-
from psycopg2 import sql
1918
from psycopg2.extensions import TransactionRollbackError
2019

2120
from odoo import api, fields, models

runbot/models/build_config.py

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353

5454

5555
def echo(text):
56-
return f'echo $(date -u "+%Y-%m-%d %H:%M:%S,%3N") {text}'
56+
return f'echo $(date -u "+%Y-%m-%d %H:%M:%S,%3N") "{text}"'
5757

5858

5959
def filter_all_modules(selector, build, dynamic_vars):
@@ -782,7 +782,7 @@ def _run_install_odoo(self, build, config_data=None):
782782
db_suffix = re.sub(r'[^a-z0-9\-_]', '_', db_suffix.lower())
783783
db_name = '%s-%s' % (build.dest, db_suffix)
784784
cmd += ['-d', db_name]
785-
self.env['runbot.database'].create({'name': db_name, 'build_id': self.id})
785+
self.env['runbot.database'].create({'name': db_name, 'build_id': build.id})
786786

787787
# Demo data behavior changed in 18.1 -> demo data became opt-in instead of opt-out
788788
available_options = build._parse_config()
@@ -843,15 +843,18 @@ def _run_install_odoo(self, build, config_data=None):
843843

844844
cmd.finals.extend(self._post_install_commands(build, config_data, py_version)) # coverage post, extra-checks, ...
845845

846+
env_variables = self.additionnal_env.split(';') if self.additionnal_env else []
847+
if config_env_variables := config_data.get('env_variables', False):
848+
env_variables += config_env_variables.split(';')
849+
846850
if config_data.get('export_database', True):
847851
self._add_zip_generation(build, cmd, db_name)
852+
env_variables.append('SAVE_CLUSTER=1')
853+
env_variables.append(f'CLUSTER_BACKUP_NAME={db_name}-cluster.zip')
848854

849855
if self.flamegraph:
850856
cmd.finals.append(['flamegraph.pl', '--title', 'Flamegraph %s for build %s' % (self.sanitized_name(build), build.id), self._perfs_data_path(build), '>', self._perfs_data_path(ext='svg')])
851857
cmd.finals.append(['gzip', '-f', self._perfs_data_path(build)]) # keep data but gz them to save disc space
852-
env_variables = self.additionnal_env.split(';') if self.additionnal_env else []
853-
if config_env_variables := config_data.get('env_variables', False):
854-
env_variables += config_env_variables.split(';')
855858

856859
if config_data.get('coverage_test_context', self.coverage_test_context):
857860
env_variables.append("COVERAGE_DYNAMIC_CONTEXT=test_function")
@@ -871,7 +874,7 @@ def _add_zip_generation(self, build, cmd, db_name):
871874
cmd.finals.append(['pg_dump', db_name, '>', sql_dest])
872875
cmd.finals.append([echo('### Copying filestore')])
873876
cmd.finals.append(['cp', '-r', filestore_path, filestore_dest])
874-
cmd.finals.append([echo('### Generaing zip')])
877+
cmd.finals.append([echo('### Generating zip')])
875878
cmd.finals.append(['cd', dump_dir, '&&', 'zip', '-rmq9', zip_path, '*'])
876879
cmd.finals.append([echo('### Done')])
877880
infos = '{\n "db_name": "%s",\n "build_id": %s,\n "shas": [%s]\n}' % (db_name, build.id, ', '.join(['"%s"' % build_commit.commit_id.dname for build_commit in build.params_id.commit_link_ids]))
@@ -1130,6 +1133,7 @@ def _run_restore(self, build, config_data=None):
11301133
dump_db = params.dump_db
11311134
if dump_url := config_data.get('dump_url'):
11321135
zip_name = dump_url.split('/')[-1]
1136+
download_db_name = zip_name.replace('.zip', '')
11331137
build._log('_run_restore', f'Restoring db [{zip_name}]({dump_url})', log_type='markdown')
11341138
else:
11351139
reference_build = None
@@ -1176,30 +1180,46 @@ def _run_restore(self, build, config_data=None):
11761180

11771181
icp = self.env['ir.config_parameter']
11781182
db_template = icp.get_param('runbot.runbot_db_template', default='template0')
1179-
self.env['runbot.database'].create({'name': restore_db_name, 'build_id': self.id})
1180-
cmd = ' && '.join([
1181-
'mkdir /data/build/restore',
1182-
'cd /data/build/restore',
1183-
echo('### getting archive'),
1184-
'wget --retry-on-host-error %s' % dump_url,
1185-
'unzip -q %s' % zip_name,
1186-
echo('### restoring filestore'),
1187-
'mkdir -p /data/build/datadir/filestore/%s' % restore_db_name,
1188-
'mv filestore/* /data/build/datadir/filestore/%s' % restore_db_name,
1189-
echo('### restoring db'),
1190-
'createdb %s -T %s' % (restore_db_name, db_template),
1191-
'psql -q %s < dump.sql' % (restore_db_name),
1192-
echo('### performing an analyze'),
1193-
'psql -q -d %s -c "ANALYZE;"' % restore_db_name,
1194-
'cd /data/build',
1195-
echo('### cleaning'),
1196-
'rm -r restore',
1197-
echo('### listing modules'),
1198-
"""psql %s -c "select name from ir_module_module where state = 'installed'" -t -A > /data/build/logs/restore_modules_installed.txt""" % restore_db_name,
1199-
echo('### restore" "successful'), # two part string to avoid miss grep
1200-
])
1201-
1202-
return dict(cmd=cmd, network_enabled=True)
1183+
self.env['runbot.database'].create({'name': restore_db_name, 'build_id': build.id})
1184+
restore_cluster = True # todo define is it is applicable
1185+
env_variables = []
1186+
if restore_cluster:
1187+
cluster_zip_url = dump_url.replace('.zip', '-cluster.zip')
1188+
env_variables.append(f'RESTORE_ZIP_URL={cluster_zip_url}')
1189+
cmd = ' && '.join([
1190+
echo('### Moving filestore'),
1191+
f'mv /data/build/datadir/filestore/{download_db_name} /data/build/datadir/filestore/{restore_db_name}',
1192+
echo('### Renaming db'),
1193+
f'psql -q -d postgres -c \'ALTER DATABASE "{download_db_name}" RENAME TO "{restore_db_name}";\'',
1194+
echo('### Performing an analyze'),
1195+
f'psql -q -d {restore_db_name} -c "ANALYZE;"',
1196+
f"""psql {restore_db_name} -c "select name from ir_module_module where state = 'installed'" -t -A > /data/build/logs/restore_modules_installed.txt""",
1197+
echo('### restore" "successful'), # two part string to avoid miss grep
1198+
])
1199+
else:
1200+
cmd = ' && '.join([
1201+
'mkdir /data/build/restore',
1202+
'cd /data/build/restore',
1203+
echo('### getting archive'),
1204+
'wget --retry-on-host-error %s' % dump_url,
1205+
'unzip -q %s' % zip_name,
1206+
echo('### restoring filestore'),
1207+
'mkdir -p /data/build/datadir/filestore/%s' % restore_db_name,
1208+
'mv filestore/* /data/build/datadir/filestore/%s' % restore_db_name,
1209+
echo('### restoring db'),
1210+
'createdb %s -T %s' % (restore_db_name, db_template),
1211+
'psql -q %s < dump.sql' % (restore_db_name),
1212+
echo('### performing an analyze'),
1213+
'psql -q -d %s -c "ANALYZE;"' % restore_db_name,
1214+
'cd /data/build',
1215+
echo('### cleaning'),
1216+
'rm -r restore',
1217+
echo('### listing modules'),
1218+
"""psql %s -c "select name from ir_module_module where state = 'installed'" -t -A > /data/build/logs/restore_modules_installed.txt""" % restore_db_name,
1219+
echo('### restore" "successful'), # two part string to avoid miss grep
1220+
])
1221+
1222+
return dict(cmd=cmd, network_enabled=True, env_variables=env_variables)
12031223

12041224
def _log_end(self, build):
12051225
# TODO fixme config data are not the same as the run part in dynamic steps

0 commit comments

Comments
 (0)