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 450146 f3353aab033e65d47431f486fd04009bb6214261
parent 450145 7ea780c325e43a9e594e015a58ff10b00970ebb5
child 450147 d2538a8c3b47faf11c04cfbb25b24bb3f1b08781
push id185
push usermozilla@hocat.ca
push dateFri, 21 Sep 2018 23:01:13 +0000
treeherdermozilla-esr60@1bb58a2125e2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbstack
bugs1487500
milestone60.2.2
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
taskcluster/docs/actions.rst
taskcluster/taskgraph/actions/create_interactive.py
taskcluster/taskgraph/actions/registry.py
--- a/taskcluster/docs/actions.rst
+++ b/taskcluster/docs/actions.rst
@@ -198,16 +198,17 @@ with the `graph configuration <taskgraph
 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 = [
     re.compile(r'^docker-worker:relengapi-proxy:tooltool.download.public$'),
     # 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`.
     re.compile(r'^secrets:get:project/releng/gecko/build/level-[0-9]/\*'),
 ]
 
 
+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'}],
+
+
 @register_callback_action(
     title='Create Interactive Task',
     name='create-interactive',
     symbol='create-inter',
     kind='hook',
     generic=True,
     description=(
         'Create a a copy of the task that you can interact with'
     ),
     order=50,
-    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,
     schema={
         '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