From 87c794a9aaac58fc2a0f214f7cdf85b58a3ce19c Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Fri, 28 Jun 2019 12:17:04 +0200 Subject: [PATCH] [IMP] runbot: add wake up option on builds --- runbot/__manifest__.py | 2 +- runbot/controllers/frontend.py | 12 +++--- runbot/migrations/4.4/post-migration.py | 5 +++ runbot/models/build.py | 34 ++++++++++++++--- runbot/models/repo.py | 6 +-- runbot/static/src/js/runbot.js | 10 +++++ runbot/templates/build.xml | 51 ++++++++++++++----------- runbot/templates/dashboard.xml | 1 - runbot/templates/frontend.xml | 18 --------- runbot/tests/test_frontend.py | 4 +- runbot/tests/test_schedule.py | 2 +- runbot/views/build_views.xml | 1 - 12 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 runbot/migrations/4.4/post-migration.py diff --git a/runbot/__manifest__.py b/runbot/__manifest__.py index 71a9d147..dfcd6b45 100644 --- a/runbot/__manifest__.py +++ b/runbot/__manifest__.py @@ -6,7 +6,7 @@ 'author': "Odoo SA", 'website': "http://runbot.odoo.com", 'category': 'Website', - 'version': '4.3', + 'version': '4.4', 'depends': ['website', 'base'], 'data': [ 'security/runbot_security.xml', diff --git a/runbot/controllers/frontend.py b/runbot/controllers/frontend.py index d918342c..2169e61f 100644 --- a/runbot/controllers/frontend.py +++ b/runbot/controllers/frontend.py @@ -45,10 +45,7 @@ class Runbot(Controller): build_ids = [] if repo: - # FIXME or removeme (filters are broken) - filters = {key: kwargs.get(key, '1') for key in ['waiting', 'pending', 'testing', 'running', 'done', 'deathrow']} domain = [('repo_id', '=', repo.id)] - domain += [('global_state', '!=', key) for key, value in iter(filters.items()) if value == '0'] if search: search_domain = [] for to_search in search.split("|"): @@ -113,8 +110,7 @@ class Runbot(Controller): 'testing': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'testing')]), 'running': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'running')]), 'pending': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'pending')]), - 'qu': QueryURL('/runbot/repo/' + slug(repo), search=search, refresh=refresh, **filters), - 'filters': filters, + 'qu': QueryURL('/runbot/repo/' + slug(repo), search=search, refresh=refresh), 'fqdn': fqdn(), }) @@ -138,6 +134,12 @@ class Runbot(Controller): build._ask_kill() return werkzeug.utils.redirect('/runbot/repo/%s' % build.repo_id.id + ('?search=%s' % search if search else '')) + @route(['/runbot/build//wakeup'], type='http', auth="user", methods=['POST'], csrf=False) + def build_wake_up(self, build_id, search=None, **post): + build = request.env['runbot.build'].sudo().browse(build_id) + build._wake_up() + return werkzeug.utils.redirect('/runbot/repo/%s' % build.repo_id.id + ('?search=%s' % search if search else '')) + @route([ '/runbot/build//force', '/runbot/build//force/', diff --git a/runbot/migrations/4.4/post-migration.py b/runbot/migrations/4.4/post-migration.py new file mode 100644 index 00000000..a805fc95 --- /dev/null +++ b/runbot/migrations/4.4/post-migration.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + + +def migrate(cr, version): + cr.execute("UPDATE runbot_build SET requested_action='deathrow', local_state='testing' WHERE local_state = 'deathrow'") diff --git a/runbot/models/build.py b/runbot/models/build.py index c424ff76..9f556182 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -21,7 +21,7 @@ from subprocess import CalledProcessError _logger = logging.getLogger(__name__) result_order = ['ok', 'warn', 'ko', 'skipped', 'killed', 'manually_killed'] -state_order = ['pending', 'testing', 'waiting', 'running', 'deathrow', 'duplicate', 'done'] +state_order = ['pending', 'testing', 'waiting', 'running', 'duplicate', 'done'] def make_selection(array): @@ -57,6 +57,8 @@ class runbot_build(models.Model): local_result = fields.Selection(make_selection(result_order), string='Build Result', oldname='result') triggered_result = fields.Selection(make_selection(result_order), string='Triggered Result') # triggered by db only + requested_action = fields.Selection([('wake_up', 'To wake up'), ('deathrow', 'To kill')], string='Action requested', index=True) + nb_pending = fields.Integer("Number of pending in queue", default=0) nb_testing = fields.Integer("Number of test slot use", default=0) nb_running = fields.Integer("Number of test slot use", default=0) @@ -402,7 +404,7 @@ class runbot_build(models.Model): else: sequence = self.search([], order='id desc', limit=1)[0].id # Force it now - if build.local_state in ['running', 'done', 'duplicate', 'deathrow']: + if build.local_state in ['running', 'done', 'duplicate']: values = { 'sequence': sequence, 'branch_id': build.branch_id.id, @@ -533,10 +535,21 @@ class runbot_build(models.Model): for build in self: self.env.cr.commit() # commit between each build to minimise transactionnal errors due to state computations self.invalidate_cache() - if build.local_state == 'deathrow': + if build.requested_action == 'deathrow': build._kill(result='manually_killed') continue + if build.requested_action == 'wake_up': + if docker_is_running(build._get_docker_name()): + build.write({'requested_action': False, 'local_state': 'running'}) + build._log('wake_up', 'Waking up failed, docker is already running', level='SEPARATOR') + else: + log_path = build._path('logs', 'wake_up.txt') + build.write({'job_start': now(), 'job_end': False, 'active_step': False, 'requested_action': False, 'local_state': 'running'}) + build._log('wake_up', 'Waking up build', level='SEPARATOR') + self.env['runbot.build.config.step']._run_odoo_run(build, log_path) + continue + if build.local_state == 'pending': # allocate port and schedule first job port = self._find_port() @@ -789,7 +802,7 @@ class runbot_build(models.Model): continue build._log('kill', 'Kill build %s' % build.dest) docker_stop(build._get_docker_name()) - v = {'local_state': 'done', 'active_step': False, 'duplicate_id': False, 'build_end': now()} # what if duplicate? state done? + v = {'local_state': 'done', 'requested_action': False, 'active_step': False, 'duplicate_id': False, 'build_end': now()} # what if duplicate? state done? if not build.job_end: v['job_end'] = now() if result: @@ -815,12 +828,19 @@ class runbot_build(models.Model): build._skip() build._log('_ask_kill', 'Skipping build %s, requested by %s (user #%s)' % (build.dest, user.name, uid)) elif build.local_state in ['testing', 'running']: - build.write({'local_state': 'deathrow'}) + build.requested_action = 'deathrow' build._log('_ask_kill', 'Killing build %s, requested by %s (user #%s)' % (build.dest, user.name, uid)) for child in build.children_ids: # should we filter build that are target of a duplicate_id? if not child.duplicate_id: child._ask_kill() + def _wake_up(self): + build = self.real_build + if build.local_state != 'done': + build._log('wake_up', 'Impossibe to wake up, state is not done') + else: + build.requested_action = 'wake_up' + def _get_all_commit(self): return [Commit(self.repo_id, self.name)] + [Commit(dep._get_repo(), dep.dependency_hash) for dep in self.dependency_ids] @@ -939,6 +959,10 @@ class runbot_build(models.Model): if not step_ids: # no job to do, build is done return {'active_step': False, 'local_state': 'done'} + if not self.active_step and self.local_state != 'pending': + # means that a step has been run manually without using config + return {'active_step': False, 'local_state': 'done'} + next_index = step_ids.index(self.active_step) + 1 if self.active_step else 0 if next_index >= len(step_ids): # final job, build is done return {'active_step': False, 'local_state': 'done'} diff --git a/runbot/models/repo.py b/runbot/models/repo.py index 15c11412..9b4b6e12 100644 --- a/runbot/models/repo.py +++ b/runbot/models/repo.py @@ -303,7 +303,7 @@ class runbot_repo(models.Model): ]) for btk in builds_to_kill: btk._log('repo._update_git', 'Build automatically killed, newer build found.', level='WARNING') - builds_to_kill.write({'local_state': 'deathrow'}) + builds_to_kill.write({'requested_action': 'deathrow'}) new_build = Build.create(build_info) # create a reverse dependency build if needed @@ -409,7 +409,7 @@ class runbot_repo(models.Model): domain_host = domain + [('host', '=', host)] # schedule jobs (transitions testing -> running, kill jobs, ...) - build_ids = Build.search(domain_host + [('local_state', 'in', ['testing', 'running', 'deathrow'])]) + build_ids = Build.search(domain_host + ['|', ('local_state', 'in', ['testing', 'running']), ('requested_action', 'in', ['wake_up', 'deathrow'])]) build_ids._schedule() self.env.cr.commit() self.invalidate_cache() @@ -467,7 +467,7 @@ class runbot_repo(models.Model): pending_build._schedule() # terminate and reap doomed build - build_ids = Build.search(domain_host + [('local_state', '=', 'running')]).ids + build_ids = Build.search(domain_host + [('local_state', '=', 'running')], order='job_start desc').ids # sort builds: the last build of each sticky branch then the rest sticky = {} non_sticky = [] diff --git a/runbot/static/src/js/runbot.js b/runbot/static/src/js/runbot.js index afa30eaf..ebd0274d 100644 --- a/runbot/static/src/js/runbot.js +++ b/runbot/static/src/js/runbot.js @@ -31,5 +31,15 @@ return false; }); }); + $(function() { + $('a.runbot-wakeup').click(function() { + var $f = $('
'), + url = _.str.sprintf('/runbot/build/%s/wakeup', $(this).data('runbot-build')) + window.location.search; + $f.attr('action', url); + $f.appendTo($('body')); + $f.submit(); + return false; + }); + }); })(jQuery); diff --git a/runbot/templates/build.xml b/runbot/templates/build.xml index 3ba4ce60..d303b0ec 100644 --- a/runbot/templates/build.xml +++ b/runbot/templates/build.xml @@ -2,26 +2,28 @@