summaryrefslogtreecommitdiffstats
path: root/taskcluster/taskgraph/actions/retrigger.py
diff options
context:
space:
mode:
Diffstat (limited to 'taskcluster/taskgraph/actions/retrigger.py')
-rw-r--r--taskcluster/taskgraph/actions/retrigger.py176
1 files changed, 173 insertions, 3 deletions
diff --git a/taskcluster/taskgraph/actions/retrigger.py b/taskcluster/taskgraph/actions/retrigger.py
index 8580231..402e59a 100644
--- a/taskcluster/taskgraph/actions/retrigger.py
+++ b/taskcluster/taskgraph/actions/retrigger.py
@@ -6,6 +6,8 @@
from __future__ import absolute_import, print_function, unicode_literals
+import sys
+
import logging
import textwrap
@@ -18,15 +20,17 @@ from .util import (
create_task_from_def,
)
from .registry import register_callback_action
+from ..util import taskcluster
logger = logging.getLogger(__name__)
+RERUN_STATES = ('exception', 'failed')
+
@register_callback_action(
title='Retrigger',
name='retrigger',
symbol='rt',
- kind='hook',
cb_name='retrigger-decision',
description=textwrap.dedent('''\
Create a clone of the task (retriggering decision, action, and cron tasks requires
@@ -53,13 +57,12 @@ def retrigger_decision_action(parameters, graph_config, input, task_group_id, ta
title='Retrigger',
name='retrigger',
symbol='rt',
- kind='hook',
generic=True,
description=(
'Create a clone of the task.'
),
order=19, # must be greater than other orders in this file, as this is the fallback version
- context=[{}],
+ context=[{'retrigger': 'true'}],
schema={
'type': 'object',
'properties': {
@@ -82,6 +85,48 @@ def retrigger_decision_action(parameters, graph_config, input, task_group_id, ta
}
}
)
+@register_callback_action(
+ title='Retrigger (disabled)',
+ name='retrigger',
+ cb_name='retrigger-disabled',
+ symbol='rt',
+ generic=True,
+ description=(
+ 'Create a clone of the task.\n\n'
+ 'This type of task should typically be re-run instead of re-triggered.'
+ ),
+ order=20, # must be greater than other orders in this file, as this is the fallback version
+ context=[{}],
+ schema={
+ 'type': 'object',
+ 'properties': {
+ 'downstream': {
+ 'type': 'boolean',
+ 'description': (
+ 'If true, downstream tasks from this one will be cloned as well. '
+ 'The dependencies will be updated to work with the new task at the root.'
+ ),
+ 'default': False,
+ },
+ 'times': {
+ 'type': 'integer',
+ 'default': 1,
+ 'minimum': 1,
+ 'maximum': 100,
+ 'title': 'Times',
+ 'description': 'How many times to run each task.',
+ },
+ 'force': {
+ 'type': 'boolean',
+ 'default': False,
+ 'description': (
+ 'This task should not be re-triggered. '
+ 'This can be overridden by passing `true` here.'
+ ),
+ },
+ }
+ }
+)
def retrigger_action(parameters, graph_config, input, task_group_id, task_id, task):
decision_task_id, full_task_graph, label_to_taskid = fetch_graph_and_labels(
parameters, graph_config)
@@ -91,6 +136,15 @@ def retrigger_action(parameters, graph_config, input, task_group_id, task_id, ta
with_downstream = ' '
to_run = [label]
+ if not input.get('force', None) and not full_task_graph[label].attributes.get('retrigger'):
+ logger.info(
+ "Not retriggering task {}, task should not be retrigged "
+ "and force not specified.".format(
+ label
+ )
+ )
+ sys.exit(1)
+
if input.get('downstream'):
to_run = full_task_graph.graph.transitive_closure(set(to_run), reverse=True).nodes
to_run = to_run & set(label_to_taskid.keys())
@@ -102,3 +156,119 @@ def retrigger_action(parameters, graph_config, input, task_group_id, task_id, ta
logger.info('Scheduled {}{}(time {}/{})'.format(label, with_downstream, i+1, times))
combine_task_graph_files(list(range(times)))
+
+
+@register_callback_action(
+ title='Rerun',
+ name='rerun',
+ generic=True,
+ symbol='rr',
+ description=(
+ 'Rerun a task.\n\n'
+ 'This only works on failed or exception tasks in the original taskgraph,'
+ ' and is CoT friendly.'
+ ),
+ order=300,
+ context=[{}],
+ schema={
+ 'type': 'object',
+ 'properties': {}
+ }
+)
+def rerun_action(parameters, graph_config, input, task_group_id, task_id, task):
+ parameters = dict(parameters)
+ decision_task_id, full_task_graph, label_to_taskid = fetch_graph_and_labels(
+ parameters, graph_config)
+ label = task['metadata']['name']
+ if task_id not in label_to_taskid.values():
+ logger.error(
+ "Refusing to rerun {}: taskId {} not in decision task {} label_to_taskid!".format(
+ label, task_id, decision_task_id
+ )
+ )
+
+ _rerun_task(task_id, label)
+
+
+def _rerun_task(task_id, label):
+ status = taskcluster.status_task(task_id)
+ if status not in RERUN_STATES:
+ logger.warning(
+ "No need to to rerun {}: state '{}' not in {}!".format(label, status, RERUN_STATES)
+ )
+ return
+ taskcluster.rerun_task(task_id)
+ logger.info('Reran {}'.format(label))
+
+
+@register_callback_action(
+ title='Retrigger',
+ name='retrigger-multiple',
+ symbol='rt',
+ generic=True,
+ description=(
+ 'Create a clone of the task.'
+ ),
+ context=[],
+ schema={
+ "type": "object",
+ "properties": {
+ "requests": {
+ "type": "array",
+ "items": {
+ "tasks": {
+ "type": "array",
+ 'description': 'An array of task labels',
+ 'items': {
+ 'type': 'string'
+ }
+ },
+ "times": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 100,
+ "title": "Times",
+ "description": "How many times to run each task.",
+ },
+ "additionalProperties": False,
+ },
+ },
+ "additionalProperties": False,
+ },
+ },
+)
+def retrigger_multiple(parameters, graph_config, input, task_group_id, task_id, task):
+ decision_task_id, full_task_graph, label_to_taskid = fetch_graph_and_labels(
+ parameters, graph_config)
+
+ suffixes = []
+ for i, request in enumerate(input.get('requests', [])):
+ times = request.get('times', 1)
+ rerun_tasks = [
+ label for label in request.get('tasks')
+ if not full_task_graph[label].attributes.get('retrigger')]
+ retrigger_tasks = [
+ label for label in request.get('tasks')
+ if full_task_graph[label].attributes.get('retrigger')
+ ]
+
+ for label in rerun_tasks:
+ # XXX we should not re-run tasks pulled in from other pushes
+ # In practice, this shouldn't matter, as only completed tasks
+ # are pulled in from other pushes and treeherder won't pass
+ # those labels.
+ _rerun_task(label_to_taskid[label], label)
+
+ for j in xrange(times):
+ suffix = '{}-{}'.format(i, j)
+ suffixes.append(suffix)
+ create_tasks(
+ retrigger_tasks,
+ full_task_graph,
+ label_to_taskid,
+ parameters,
+ decision_task_id,
+ suffix,
+ )
+
+ combine_task_graph_files(suffixes)