Skip to content

Commit ab2cff5

Browse files
committed
feat: verify decision task has sufficient scopes before submitting
1 parent 42a10da commit ab2cff5

File tree

3 files changed

+50
-0
lines changed

3 files changed

+50
-0
lines changed

src/taskgraph/decision.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from taskgraph.util.python_path import find_object
2323
from taskgraph.util.schema import Schema, validate_schema
2424
from taskgraph.util.vcs import Repository, get_repository
25+
from taskgraph.util.verify import verifications
2526
from taskgraph.util.yaml import load_yaml
2627

2728
logger = logging.getLogger(__name__)
@@ -123,6 +124,9 @@ def taskgraph_decision(options, parameters=None):
123124
shutil.copy2(RUN_TASK_DIR / "run-task", ARTIFACTS_DIR)
124125
shutil.copy2(RUN_TASK_DIR / "fetch-content", ARTIFACTS_DIR)
125126

127+
# run 'decision' verifications
128+
verifications("decision", tgg.morphed_task_graph, tgg.graph_config, tgg.parameters)
129+
126130
# actually create the graph
127131
create_tasks(
128132
tgg.graph_config,

src/taskgraph/util/taskcluster.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ def rerun_task(task_id):
309309
_do_request(get_task_url(task_id, use_proxy=True) + "/rerun", json={})
310310

311311

312+
@memoize
312313
def get_current_scopes():
313314
"""Get the current scopes. This only makes sense in a task with the Taskcluster
314315
proxy enabled, where it returns the actual scopes accorded to the task."""

src/taskgraph/util/verify.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import logging
77
import sys
88
from abc import ABC, abstractmethod
9+
from textwrap import dedent
910

1011
import attr
1112

1213
from taskgraph.config import GraphConfig
1314
from taskgraph.parameters import Parameters
1415
from taskgraph.taskgraph import TaskGraph
1516
from taskgraph.util.attributes import match_run_on_projects
17+
from taskgraph.util.taskcluster import get_current_scopes
1618
from taskgraph.util.treeherder import join_symbol
1719

1820
logger = logging.getLogger(__name__)
@@ -281,3 +283,46 @@ def verify_always_optimized(task, taskgraph, scratch_pad, graph_config, paramete
281283
return
282284
if task.task.get("workerType") == "always-optimized":
283285
raise Exception(f"Could not optimize the task {task.label!r}")
286+
287+
288+
@verifications.add("decision")
289+
def verify_scopes_satisfaction(task, taskgraph, scratch_pad, graph_config, parameters):
290+
if task is None:
291+
if not scratch_pad:
292+
return
293+
294+
s = "s" if len(scratch_pad) else ""
295+
are = "are" if len(scratch_pad) else "is"
296+
297+
failstr = ""
298+
for label, scopes in scratch_pad.items():
299+
failstr += "\n" + f" {label}:"
300+
failstr += (
301+
" \n" + "\n ".join([f" {s}" for s in sorted(scopes)]) + "\n"
302+
)
303+
304+
msg = dedent(
305+
f"""
306+
Required scopes are missing!
307+
308+
The Decision task does not have all of the scopes necessary to
309+
perform this request. The following task{s} {are} requesting scopes
310+
the Decision task does not have:
311+
"""
312+
)
313+
msg += failstr
314+
raise Exception(msg)
315+
316+
current_scopes = get_current_scopes()
317+
missing = set()
318+
for required in task.task["scopes"]:
319+
for current in current_scopes:
320+
if current == required:
321+
break
322+
if current[-1] == "*" and required.startswith(current[:-1]):
323+
break
324+
else:
325+
missing.add(required)
326+
327+
if missing:
328+
scratch_pad[task.label] = sorted(missing)

0 commit comments

Comments
 (0)