357753156ca0 — Steve Fink 3 months ago
[run-taskcluster-job] Add in new tool for duplicating a taskcluster job locally via docker
2 files changed, 89 insertions(+), 0 deletions(-)

M README.md
A => bin/run-taskcluster-job
M README.md +25 -0
@@ 8,6 8,7 @@ Tools included:
 
  - landed : Prune changesets that have landed, setting their successors to the landed
    revisions.
+ - run-taskcluster-image : Run taskcluster jobs in a local Docker container.
  - get-taskcluster-logs : Retrieve groups of log files from a push by scraping taskcluster
  - json : Interactive navigation of a JSON file
  - debug : Start up a debugger within emacs on various types of files

          
@@ 74,6 75,30 @@ need to rebase them (eg by running `hg e
 
 ----------------------------------------------------------------------
 
+run-taskcluster-image : Run taskcluster jobs in a local Docker container.
+
+    run-taskcluster-image --log-task-id a5gT2XbUSGuBd-IMAjjTUw
+
+to replicate task a5gT2XbUSGuBd-IMAjjTUw locally. The above command will
+
+ - download the log file for that task
+ - find the line that says the task ID of the toolchain task that generated the
+   image that it is running
+ - use `mach taskcluster-load-image` to pull down that image
+ - once you have the image, use `--task-id` in later runs to avoid re-downloading things
+ - download the task description from taskcluster to extract out the command that
+   is to be executed and the environment variables
+ - execute the image (run `$COMMAND` from within the image to run the default command,
+   or `echo $COMMAND` to inspect and modify it.)
+
+Note that $COMMAND will probably execute `run-task` with a gecko revision,
+which will start out by pulling down the whole tree. This is large and will
+take a while. (Avoiding this requires hacking the script a bit;
+https://bugzilla.mozilla.org/show_bug.cgi?id=1605232 was an early attempt at
+that.)
+
+----------------------------------------------------------------------
+
 get-taskcluster-logs - Retrieve groups of log files from a push by scraping taskcluster
 
 Typical example: copy link location to a taskcluster push (what you get from

          
A => bin/run-taskcluster-job +64 -0
@@ 0,0 1,64 @@ 
+#!/usr/bin/python
+
+import argparse
+import os
+import re
+import requests
+import subprocess
+import shlex
+import sys
+
+parser = argparse.ArgumentParser("run a taskcluster image")
+parser.add_argument("--log-task-id")
+parser.add_argument("--load-task-id")
+parser.add_argument("--task-id")
+parser.add_argument("--image", default="docker.io/library/debian10-amd64-build:latest")
+parser.add_argument("--env-file")
+args = parser.parse_args()
+
+if args.log_task_id:
+    print("Grabbing the log file for a run of a task and extracting the docker image task ID")
+    log_url = f"https://firefoxci.taskcluster-artifacts.net/{args.log_task_id}/0/public/logs/live_backing.log"
+    log = requests.get(log_url)
+    m = re.search(r'Downloading artifact "public/image.tar.zst" from task ID: (.*)\.\n', log)
+    if not m:
+        print("Could not find image download line in log file")
+        sys.exit(1)
+
+    args.load_task_id = m.group(1)
+    args.task_id = args.log_task_id
+
+if args.load_task_id:
+    print("Loading taskcluster image")
+    out = subprocess.check_output(["mach", "taskcluster-load-image",
+                                   "--task-id", args.load_task_id])
+    m = re.search(r'Loaded image: (\S+)', out)
+    if m:
+        args.image = m.group(1)
+
+if args.task_id and not args.env_file:
+    args.env_file = "/tmp/task_env.sh"
+    print(f"Extracting env settings from task and storing in {args.env_file}")
+    task = requests.get(f"https://firefox-ci-tc.services.mozilla.com/api/queue/v1/task/{args.task_id}").json()
+    payload = task["payload"]
+    env = payload["env"]
+    with open(args.env_file, "wt") as fh:
+        for k, v in env.items():
+            print(f"export {k}={shlex.quote(v)}", file=fh)
+        print(f"export COMMAND={shlex.quote(shlex.join(payload['command']))}", file=fh)
+
+if args.image:
+    print("Running docker image")
+    cmd = [
+        "docker", "run", "-ti",
+        "--cap-add=SYS_PTRACE",
+        "--security-opt", "seccomp=unconfined",
+    ]
+    if args.env_file:
+        print("Note that the command will be stored in the $COMMAND env var")
+        print("Once the shell starts, it can be executed by typing $COMMAND:")
+        print("  bash# $COMMAND")
+        cmd += ["-v", f"{args.env_file}:/etc/profile.d/task.sh:z"]
+    cmd += ["-v", "/home/sfink/bin:/usr/local/bin:z"]
+    cmd += [args.image, "bash", "-l"]
+    subprocess.call(cmd)