Bug 1487500: allow context to be different based on parameters r=bstack
authorDustin J. Mitchell <dustin@mozilla.com>
Mon, 17 Sep 2018 23:29:28 +0000
changeset 481182 04225268cddf07f2864d407560bbd53bb36629e4
parent 481181 3043bc2a9bbfc8916b3b567ea1ef37ad4e454c9d
child 481183 22d8343c70a09ca8c4c8f592e01ec26269319b2d
push id1799
push usermozilla@hocat.ca
push dateFri, 21 Sep 2018 21:40:19 +0000
treeherdermozilla-release@aa7b44bb25aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
Bug 1487500: allow context to be different based on parameters r=bstack ..and use this to allow create-interactive on level 3, but only for testers. Differential Revision: https://phabricator.services.mozilla.com/D5573
--- a/taskcluster/docs/actions.rst
+++ b/taskcluster/docs/actions.rst
@@ -198,16 +198,17 @@ with the `graph configuration <taskgraph
 Once you have specified input and context as applicable for your action you can
 do pretty much anything you want from within your callback. Whether you want
 to create one or more tasks or run a specific piece of code like a test.
 Conditional Availability
 The decision parameters ``taskgraph.parameters.Parameters`` passed to
 the callback are also available when the decision task generates the list of
 actions to be displayed in the user interface. When registering an action
 callback the ``availability`` option can be used to specify a callable
 which, given the decision parameters, determines if the action should be available.
 The feature is illustrated below::
   from registry import register_callback_action
@@ -223,16 +224,22 @@ The feature is illustrated below::
   def try_only_action(parameters, graph_config, input, task_group_id, task_id, task):
       print "My try-only action"
 Properties of ``parameters``  are documented in the
 :doc:`parameters section <parameters>`. You can also examine the
 ``parameters.yml`` artifact created by decisions tasks.
+Context can be similarly conditionalized by passing a function which returns
+the appropriate context::
+    context=lambda params:
+        [{}] if int(params['level']) < 3 else [{'worker-implementation': 'docker-worker'}],
 Creating Tasks
 The ``create_tasks`` utility function provides a full-featured way to create
 new tasks.  Its features include creating prerequisite tasks, operating in a
 "testing" mode with ``./mach taskgraph action-callback --test``, and generating
 artifacts that can be used by later action tasks to figure out what happened.
 See the source for more detailed docmentation.
--- a/taskcluster/taskgraph/actions/create_interactive.py
+++ b/taskcluster/taskgraph/actions/create_interactive.py
@@ -50,30 +50,36 @@ SCOPE_WHITELIST = [
     # level-appropriate secrets are generally necessary to run a task; these
     # also are "not that secret" - most of them are built into the resulting
     # binary and could be extracted by someone with `strings`.
+def context(params):
+    # available for any docker-worker tasks at levels 1, 2; and for
+    # test tasks on level 3 (level-3 builders are firewalled off)
+    if int(params['level']) < 3:
+        return [{'worker-implementation': 'docker-worker'}]
+    else:
+        return [{'worker-implementation': 'docker-worker', 'kind': 'test'}],
     title='Create Interactive Task',
         'Create a a copy of the task that you can interact with'
-    context=[{'worker-implementation': 'docker-worker'}],
-    # only available on level 1, 2 runs
-    # TODO: support tests on level 3
-    available=lambda params: int(params['level']) < 3,
+    context=context,
         'type': 'object',
         'properties': {
             'notify': {
                 'type': 'string',
                 'format': 'email',
                 'title': 'Who to notify of the pending interactive task',
                 'description': (
--- a/taskcluster/taskgraph/actions/registry.py
+++ b/taskcluster/taskgraph/actions/registry.py
@@ -94,16 +94,19 @@ def register_callback_action(name, title
         List of tag-sets specifying which tasks the action is can take as input.
         If no tag-sets is specified as input the action is related to the
         entire task-group, and won't be triggered with a given task.
         Otherwise, if ``context = [{'k': 'b', 'p': 'l'}, {'k': 't'}]`` will only
         be displayed in the context menu for tasks that has
         ``task.tags.k == 'b' && task.tags.p = 'l'`` or ``task.tags.k = 't'``.
         Esentially, this allows filtering on ``task.tags``.
+        If this is a function, it is given the decision parameters and must return
+        a value of the form described above.
     available : function
         An optional function that given decision parameters decides if the
         action is available. Defaults to a function that always returns ``True``.
     schema : dict
         JSON schema specifying input accepted by the action.
         This is optional and can be left ``null`` if no input is taken.
     kind : string
         The action kind to define - must be one of `task` or `hook`.  Only for
@@ -124,16 +127,21 @@ def register_callback_action(name, title
     mem = {"registered": False}  # workaround nonlocal missing in 2.x
     assert isinstance(title, basestring), 'title must be a string'
     assert isinstance(description, basestring), 'description must be a string'
     title = title.strip()
     description = description.strip()
+    # ensure that context is callable
+    if not callable(context):
+        context_value = context
+        context = lambda params: context_value  # noqa
     def register_callback(cb, cb_name=cb_name):
         assert isinstance(name, basestring), 'name must be a string'
         assert isinstance(order, int), 'order must be an integer'
         assert kind in ('task', 'hook'), 'kind must be task or hook'
         assert callable(schema) or is_json(schema), 'schema must be a JSON compatible object'
         assert isinstance(cb, FunctionType), 'callback must be a function'
         # Allow for json-e > 25 chars in the symbol.
         if '$' not in symbol:
@@ -179,17 +187,17 @@ def register_callback_action(name, title
                 'cb_name': cb_name,
                 'symbol': symbol,
             rv = {
                 'name': name,
                 'title': title,
                 'description': description,
-                'context': context,
+                'context': context(parameters),
             if schema:
                 rv['schema'] = schema(graph_config=graph_config) if callable(schema) else schema
             # for kind=task, we embed the task from .taskcluster.yml in the action, with
             # suitable context
             if kind == 'task':
                 # tasks get all of the scopes the original push did, yuck; this is not