Bug 1384433 - Add a verification that tiers are consistent across dependencies. r?dustin draft
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 25 Aug 2017 10:07:13 +0900
changeset 654547 0dfad6c3bc2830ef8da2cbe2bdac365301bfe6f0
parent 654546 b31c603abc67878a835ad63c870f65365869f44a
child 728584 54e52397b1599008ca01cec59b16e6064aa78cfb
push id76595
push userbmo:mh+mozilla@glandium.org
push dateMon, 28 Aug 2017 22:56:40 +0000
reviewersdustin
bugs1384433
milestone57.0a1
Bug 1384433 - Add a verification that tiers are consistent across dependencies. r?dustin
taskcluster/taskgraph/util/verify.py
--- a/taskcluster/taskgraph/util/verify.py
+++ b/taskcluster/taskgraph/util/verify.py
@@ -1,31 +1,36 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 import os
+import sys
 
 base_path = os.path.join(os.getcwd(), "taskcluster/docs/")
 
 
 class VerificationSequence(object):
     """
     Container for a sequence of verifications over a TaskGraph. Each
     verification is represented as a callable taking (task, taskgraph,
-    scratch_pad), called for each task in the taskgraph.
+    scratch_pad), called for each task in the taskgraph, and one more
+    time with no task but with the taskgraph and the same scratch_pad
+    that was passed for each task.
     """
     def __init__(self):
         self.verifications = {}
 
     def __call__(self, graph_name, graph):
         for verification in self.verifications.get(graph_name, []):
-            graph.for_each_task(verification, scratch_pad={})
+            scratch_pad = {}
+            graph.for_each_task(verification, scratch_pad=scratch_pad)
+            verification(None, graph, scratch_pad=scratch_pad)
         return graph_name, graph
 
     def add(self, graph_name):
         def wrap(func):
             self.verifications.setdefault(graph_name, []).append(func)
             return func
         return wrap
 
@@ -66,16 +71,18 @@ def verify_docs(filename, identifiers, a
 
 @verifications.add('full_task_graph')
 def verify_task_graph_symbol(task, taskgraph, scratch_pad):
     """
         This function verifies that tuple
         (collection.keys(), machine.platform, groupSymbol, symbol) is unique
         for a target task graph.
     """
+    if task is None:
+        return
     task_dict = task.task
     if "extra" in task_dict:
         extra = task_dict["extra"]
         if "treeherder" in extra:
             treeherder = extra["treeherder"]
 
             collection_keys = tuple(sorted(treeherder.get('collection', {}).keys()))
             platform = treeherder.get('machine', {}).get('platform')
@@ -93,21 +100,46 @@ def verify_task_graph_symbol(task, taskg
 
 
 @verifications.add('full_task_graph')
 def verify_gecko_v2_routes(task, taskgraph, scratch_pad):
     """
         This function ensures that any two
         tasks have distinct index.v2.routes
     """
+    if task is None:
+        return
     route_prefix = "index.gecko.v2"
     task_dict = task.task
     routes = task_dict.get('routes', [])
 
     for route in routes:
         if route.startswith(route_prefix):
             if route in scratch_pad:
                 raise Exception(
                     "conflict between {}:{} for route: {}"
                     .format(task.label, scratch_pad[route], route)
                 )
             else:
                 scratch_pad[route] = task.label
+
+
+@verifications.add('full_task_graph')
+def verify_dependency_tiers(task, taskgraph, scratch_pad):
+    tiers = scratch_pad
+    if task is not None:
+        tiers[task.label] = task.task.get('extra', {}) \
+                                     .get('treeherder', {}) \
+                                     .get('tier', sys.maxint)
+    else:
+        def printable_tier(tier):
+            if tier == sys.maxint:
+                return 'unknown'
+            return tier
+
+        for task in taskgraph.tasks.itervalues():
+            tier = tiers[task.label]
+            for d in task.dependencies.itervalues():
+                if tier < tiers[d]:
+                    raise Exception(
+                        '{} (tier {}) cannot depend on {} (tier {})'
+                        .format(task.label, printable_tier(tier),
+                                d, printable_tier(tiers[d])))