Bug 1281179 - Create a docker image for Ubuntu 16.04 for use in tests. r=dustin
authorJoel Maher <jmaher@mozilla.com>
Mon, 25 Jul 2016 10:44:37 -0400
changeset 348662 b1bc27d8f8a583ccdbbb05e1dc5829a11118d8af
parent 348661 9c839b1d1397c2f43a66aeb008f230b068f325ed
child 348663 29f104c4c3ce6d8b89d6cead50a2bdab8de42731
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdustin
bugs1281179
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1281179 - Create a docker image for Ubuntu 16.04 for use in tests. r=dustin MozReview-Commit-ID: ApHhev5J21x
taskcluster/ci/desktop-test/tests.yml
taskcluster/ci/docker-image/kind.yml
taskcluster/scripts/tester/test-linux.sh
taskcluster/scripts/tester/test-ubuntu1204.sh
taskcluster/scripts/tester/test-ubuntu1604.sh
taskcluster/taskgraph/transforms/tests/make_task_description.py
taskcluster/taskgraph/transforms/tests/test_description.py
testing/docker/desktop-test/bin/test.sh
testing/docker/desktop1604-test/Dockerfile
testing/docker/desktop1604-test/apport
testing/docker/desktop1604-test/bin/run-wizard
testing/docker/desktop1604-test/bin/test.sh
testing/docker/desktop1604-test/buildprops.json
testing/docker/desktop1604-test/deja-dup-monitor.desktop
testing/docker/desktop1604-test/dot-files/config/pip/pip.conf
testing/docker/desktop1604-test/dot-files/config/user-dirs.dirs
testing/docker/desktop1604-test/dot-files/config/user-dirs.locale
testing/docker/desktop1604-test/dot-files/hgrc
testing/docker/desktop1604-test/dot-files/pulse/default.pa
testing/docker/desktop1604-test/fonts.conf
testing/docker/desktop1604-test/jockey-gtk.desktop
testing/docker/desktop1604-test/motd
testing/docker/desktop1604-test/release-upgrades
testing/docker/desktop1604-test/taskcluster-interactive-shell
testing/docker/desktop1604-test/tc-vcs-config.yml
testing/docker/desktop1604-test/tester.env
testing/docker/ubuntu1604-test/Dockerfile
testing/docker/ubuntu1604-test/REGISTRY
testing/docker/ubuntu1604-test/VERSION
testing/docker/ubuntu1604-test/system-setup.sh
--- a/taskcluster/ci/desktop-test/tests.yml
+++ b/taskcluster/ci/desktop-test/tests.yml
@@ -269,16 +269,18 @@ mochitest-jetpack:
             - --mochitest-suite=jetpack-package
 
 mochitest-media:
     description: "Mochitest media run"
     suite: mochitest/mochitest-media
     treeherder-symbol: tc-M(mda)
     max-run-time: 5400
     loopback-video: true
+    instance-size: large
+    docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: mozharness/scripts/desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             - mozharness/configs/unittests/linux_unittest.py
             - mozharness/configs/remove_executables.py
         extra-options:
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -7,14 +7,15 @@ images_path: '../../../testing/docker'
 
 # make a task for each docker-image we might want.  For the moment, since we
 # write artifacts for each, these are whitelisted, but ideally that will change
 # (to use subdirectory clones of the proper directory), at which point we can
 # generate tasks for every docker image in the directory, secure in the
 # knowledge that unnecessary images will be omitted from the target task graph
 images:
   - desktop-test
+  - desktop1604-test
   - desktop-build
   - builder
   - tester
   - lint
   - android-gradle-build
   - phone-builder
rename from taskcluster/scripts/tester/test-linux.sh
rename to taskcluster/scripts/tester/test-ubuntu1204.sh
--- a/taskcluster/scripts/tester/test-linux.sh
+++ b/taskcluster/scripts/tester/test-ubuntu1204.sh
@@ -59,17 +59,17 @@ rm -rf mozharness
 unzip -q mozharness.zip
 rm mozharness.zip
 
 if ! [ -d mozharness ]; then
     fail "mozharness zip did not contain mozharness/"
 fi
 
 # start up the pulseaudio daemon.  Note that it's important this occur
-# before the Xvfb startup.
+# before the Xvfb startup for ubuntu 12.04, not for 16.04
 if $NEED_PULSEAUDIO; then
     pulseaudio --fail --daemonize --start
     pactl load-module module-null-sink
 fi
 
 # run XVfb in the background, if necessary
 if $NEED_XVFB; then
     Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
new file mode 100644
--- /dev/null
+++ b/taskcluster/scripts/tester/test-ubuntu1604.sh
@@ -0,0 +1,158 @@
+#! /bin/bash -xe
+
+set -x -e
+
+echo "running as" $(id)
+
+####
+# Taskcluster friendly wrapper for performing fx desktop tests via mozharness.
+####
+
+# Inputs, with defaults
+
+: MOZHARNESS_URL                ${MOZHARNESS_URL}
+: MOZHARNESS_SCRIPT             ${MOZHARNESS_SCRIPT}
+: MOZHARNESS_CONFIG             ${MOZHARNESS_CONFIG}
+: NEED_XVFB                     ${NEED_XVFB:=true}
+: NEED_WINDOW_MANAGER           ${NEED_WINDOW_MANAGER:=false}
+: NEED_PULSEAUDIO               ${NEED_PULSEAUDIO:=false}
+: START_VNC                     ${START_VNC:=false}
+: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
+: mozharness args               "${@}"
+
+set -v
+cd $WORKSPACE
+
+fail() {
+    echo # make sure error message is on a new line
+    echo "[test-linux.sh:error]" "${@}"
+    exit 1
+}
+
+# test required parameters are supplied
+if [[ -z ${MOZHARNESS_URL} ]]; then fail "MOZHARNESS_URL is not set"; fi
+if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
+if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
+
+mkdir -p ~/artifacts/public
+
+cleanup() {
+    local rv=$?
+    if [[ -s /home/worker/.xsession-errors ]]; then
+      # To share X issues
+      cp /home/worker/.xsession-errors ~/artifacts/public/xsession-errors.log
+    fi
+    # When you call this script with START_VNC we make sure we
+    # don't kill xvfb so you don't lose your VNC connection
+    if [ -n "$xvfb_pid" ] && [ $START_VNC == false ] ; then
+        kill $xvfb_pid || true
+    fi
+    exit $rv
+}
+trap cleanup EXIT INT
+
+# Unzip the mozharness ZIP file created by the build task
+if ! curl --fail -o mozharness.zip --retry 10 -L $MOZHARNESS_URL; then
+    fail "failed to download mozharness zip"
+fi
+rm -rf mozharness
+unzip -q mozharness.zip
+rm mozharness.zip
+
+if ! [ -d mozharness ]; then
+    fail "mozharness zip did not contain mozharness/"
+fi
+
+# run XVfb in the background, if necessary
+if $NEED_XVFB; then
+    Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
+       > ~/artifacts/public/xvfb.log 2>&1 &
+    export DISPLAY=:0
+    xvfb_pid=$!
+    # Only error code 255 matters, because it signifies that no
+    # display could be opened. As long as we can open the display
+    # tests should work. We'll retry a few times with a sleep before
+    # failing.
+    retry_count=0
+    max_retries=2
+    xvfb_test=0
+    until [ $retry_count -gt $max_retries ]; do
+        xvinfo || xvfb_test=$?
+        if [ $xvfb_test != 255 ]; then
+            retry_count=$(($max_retries + 1))
+        else
+            retry_count=$(($retry_count + 1))
+            echo "Failed to start Xvfb, retry: $retry_count"
+            sleep 2
+        fi
+    done
+    if [ $xvfb_test == 255 ]; then fail "xvfb did not start properly"; fi
+fi
+
+if $START_VNC; then
+    x11vnc > ~/artifacts/public/x11vnc.log 2>&1 &
+fi
+
+if $NEED_WINDOW_MANAGER; then
+    # This is read by xsession to select the window manager
+    echo DESKTOP_SESSION=ubuntu > /home/worker/.xsessionrc
+
+    # note that doing anything with this display before running Xsession will cause sadness (like,
+    # crashes in compiz). Make sure that X has enough time to start
+    sleep 15
+    # DISPLAY has already been set above
+    # XXX: it would be ideal to add a semaphore logic to make sure that the
+    # window manager is ready
+    /etc/X11/Xsession 2>&1 &
+
+    # Turn off the screen saver and screen locking
+    gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
+    gsettings set org.gnome.desktop.screensaver lock-enabled false
+    gsettings set org.gnome.desktop.screensaver lock-delay 3600
+    # Disable the screen saver
+    xset s off s reset
+
+    # start compiz for our window manager
+    compiz 2>&1 &
+
+    #TODO: how to determine if compiz starts correctly?
+fi
+
+# start up the pulseaudio daemon.  Note that it's important this occur
+# before the Xvfb startup for ubuntu 12.04, not for 16.04
+if $NEED_PULSEAUDIO; then
+    pulseaudio --fail --daemonize --start
+    pactl load-module module-null-sink
+fi
+
+# For telemetry purposes, the build process wants information about the
+# source it is running; tc-vcs obscures this a little, but we can provide
+# it directly.
+export MOZ_SOURCE_REPO="${GECKO_HEAD_REPOSITORY}"
+export MOZ_SOURCE_CHANGESET="${GECKO_HEAD_REV}"
+
+# support multiple, space delimited, config files
+config_cmds=""
+for cfg in $MOZHARNESS_CONFIG; do
+  config_cmds="${config_cmds} --config-file $WORKSPACE/${cfg}"
+done
+
+mozharness_bin="/home/worker/bin/run-mozharness"
+
+# Save the computed mozharness command to a binary which is useful
+# for interactive mode.
+echo -e "#!/usr/bin/env bash
+# Some mozharness scripts assume base_work_dir is in
+# the current working directory, see bug 1279237
+cd $WORKSPACE
+cmd=\"python2.7 $WORKSPACE/${MOZHARNESS_SCRIPT} ${config_cmds} ${@} \${@}\"
+echo \"Running: \${cmd}\"
+exec \${cmd}" > ${mozharness_bin}
+chmod +x ${mozharness_bin}
+
+# In interactive mode, the user will be prompted with options for what to do.
+if [ "$TASKCLUSTER_INTERACTIVE" != "true" ]; then
+  # run the given mozharness script and configs, but pass the rest of the
+  # arguments in from our own invocation
+  ${mozharness_bin};
+fi
--- a/taskcluster/taskgraph/transforms/tests/make_task_description.py
+++ b/taskcluster/taskgraph/transforms/tests/make_task_description.py
@@ -121,16 +121,17 @@ def docker_worker_setup(config, test, ta
     installer_url = ARTIFACT_URL.format('<build>', mozharness['build-artifact-name'])
     test_packages_url = ARTIFACT_URL.format('<build>',
                                             'public/build/target.test_packages.json')
     mozharness_url = ARTIFACT_URL.format('<build>',
                                          'public/build/mozharness.zip')
 
     taskdesc['worker-type'] = {
         'default': 'aws-provisioner-v1/desktop-test',
+        'large': 'aws-provisioner-v1/desktop-test-large',
         'xlarge': 'aws-provisioner-v1/desktop-test-xlarge',
     }[test['instance-size']]
 
     worker = taskdesc['worker'] = {}
     worker['implementation'] = test['worker-implementation']
 
     docker_image = test.get('docker-image')
     assert docker_image, "no docker image defined for a docker-worker/docker-engine task"
--- a/taskcluster/taskgraph/transforms/tests/test_description.py
+++ b/taskcluster/taskgraph/transforms/tests/test_description.py
@@ -72,18 +72,18 @@ test_description_schema = Schema({
     # and treeherder group.
     Required('e10s', default='both'): Any(
         bool, 'both',
         {'by-test-platform': {basestring: Any(bool, 'both')}},
     ),
 
     # The EC2 instance size to run these tests on.
     Required('instance-size', default='default'): Any(
-        Any('default', 'xlarge'),
-        {'by-test-platform': {basestring: Any('default', 'xlarge')}},
+        Any('default', 'large', 'xlarge'),
+        {'by-test-platform': {basestring: Any('default', 'large', 'xlarge')}},
     ),
 
     # Whether the task requires loopback audio or video (whatever that may mean
     # on the platform)
     Required('loopback-audio', default=False): bool,
     Required('loopback-video', default=False): bool,
 
     # The worker implementation for this test, as dictated by policy and by the
--- a/testing/docker/desktop-test/bin/test.sh
+++ b/testing/docker/desktop-test/bin/test.sh
@@ -24,15 +24,15 @@ fail() {
 ####
 # Now get the test-linux.sh script from the given Gecko tree and run it with
 # the same arguments.
 ####
 
 [ -d $WORKSPACE ] || mkdir -p $WORKSPACE
 cd $WORKSPACE
 
-script=taskcluster/scripts/tester/test-linux.sh
+script=taskcluster/scripts/tester/test-ubuntu1204.sh
 url=${GECKO_HEAD_REPOSITORY}/raw-file/${GECKO_HEAD_REV}/${script}
 if ! curl --fail -o ./test-linux.sh --retry 10 $url; then
     fail "failed downloading test-linux.sh from ${GECKO_HEAD_REPOSITORY}"
 fi
 chmod +x ./test-linux.sh
 exec ./test-linux.sh "${@}"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/Dockerfile
@@ -0,0 +1,72 @@
+FROM          taskcluster/ubuntu1604-test:0.1.3
+MAINTAINER    Joel Maher <joel.maher@gmail.com>
+
+# Add utilities and configuration
+COPY           dot-files/config              /home/worker/.config
+COPY           dot-files/pulse               /home/worker/.pulse
+COPY           dot-files/hgrc                /home/worker/.hgrc
+COPY           bin                           /home/worker/bin
+RUN            chmod +x bin/*
+# TODO: remove this when buildbot is gone
+COPY           buildprops.json               /home/worker/buildprops.json
+COPY           tc-vcs-config.yml /etc/taskcluster-vcs.yml
+
+# TODO: remove
+ADD            https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
+RUN chmod u+x /home/worker/bin/buildbot_step
+
+# TODO: remove
+ADD            https://s3-us-west-2.amazonaws.com/test-caching/packages/linux64-stackwalk /usr/local/bin/linux64-minidump_stackwalk
+RUN chmod +x /usr/local/bin/linux64-minidump_stackwalk
+
+# allow the worker user to access video devices
+RUN usermod -a -G video worker
+
+RUN mkdir Documents; mkdir Pictures; mkdir Music; mkdir Videos; mkdir artifacts
+
+# install a new enough npm, plus tc-vcs and tc-npm-cache
+RUN npm install -g npm@^2.0.0 \
+ && npm install -g taskcluster-vcs@2.3.12 \
+ && npm install -g taskcluster-npm-cache@1.1.14 \
+ && rm -rf ~/.npm
+ENV PATH $PATH:/home/worker/bin
+
+# Remove once running under 'worker' user.  This is necessary for pulseaudio to start
+# XXX: change this back to worker:worker once permissions issues are resolved
+RUN            chown -R root:root /home/worker
+
+
+# TODO Re-enable worker when bug 1093833 lands
+#USER          worker
+
+# clean up
+RUN rm -Rf .cache && mkdir -p .cache
+
+# Disable Ubuntu update prompt
+# http://askubuntu.com/questions/515161/ubuntu-12-04-disable-release-notification-of-14-04-in-update-manager
+ADD release-upgrades /etc/update-manager/release-upgrades
+
+# Disable tools with on-login popups that interfere with tests; see bug 1240084 and bug 984944.
+ADD jockey-gtk.desktop deja-dup-monitor.desktop /etc/xdg/autostart/
+
+# In test.sh we accept START_VNC to start a vnc daemon.
+# Exposing this port allows it to work.
+EXPOSE 5900
+
+# This helps not forgetting setting DISPLAY=:0 when running
+# tests outside of test.sh
+ENV DISPLAY :0
+
+# Disable apport (Ubuntu app crash reporter) to avoid stealing focus from test runs
+ADD apport /etc/default/apport
+
+# Disable font antialiasing for now to match releng's setup
+ADD fonts.conf /home/worker/.fonts.conf
+
+# Set up first-run experience for interactive mode
+ADD motd /etc/taskcluster-motd
+ADD taskcluster-interactive-shell /bin/taskcluster-interactive-shell
+RUN chmod +x /bin/taskcluster-interactive-shell
+
+# Set a default command useful for debugging
+CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/apport
@@ -0,0 +1,1 @@
+enabled=0
new file mode 100755
--- /dev/null
+++ b/testing/docker/desktop1604-test/bin/run-wizard
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# 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/.
+
+from __future__ import print_function, unicode_literals
+
+import os
+import subprocess
+import sys
+from textwrap import wrap
+
+
+def call(cmd, **kwargs):
+    print(" ".join(cmd))
+    return subprocess.call(cmd, **kwargs)
+
+
+def resume():
+    call(['run-mozharness'])
+
+
+def setup():
+    call(['run-mozharness', '--no-run-tests'])
+    print("Mozharness has finished downloading the build and "
+          "tests to {}.".format(os.path.join(os.getcwd(), 'build')))
+
+
+def clone():
+    repo = os.environ['GECKO_HEAD_REPOSITORY']
+    rev = os.environ['GECKO_HEAD_REV']
+    clone_path = os.path.expanduser(os.path.join('~', 'gecko'))
+
+    # try is too large to clone, instead clone central and pull
+    # in changes from try
+    if "hg.mozilla.org/try" in repo:
+        central = 'http://hg.mozilla.org/mozilla-central'
+        call(['hg', 'clone', '-U', central, clone_path])
+        call(['hg', 'pull', '-u', '-r', rev, repo], cwd=clone_path)
+    else:
+        call(['hg', 'clone', '-u', rev, repo, clone_path])
+    print("Finished cloning to {} at revision {}.".format(
+                clone_path, rev))
+
+
+def exit():
+    pass
+
+
+OPTIONS = [
+    ('Resume task', resume,
+     "Resume the original task without modification. This can be useful for "
+     "passively monitoring it from another shell."),
+    ('Setup task', setup,
+     "Setup the task (download the application and tests) but don't run the "
+     "tests just yet. The tests can be run with a custom configuration later "
+     "(experimental)."),
+    ('Clone gecko', clone,
+     "Perform a clone of gecko using the task's repo and update it to the "
+     "task's revision."),
+    ('Exit', exit, "Exit this wizard and return to the shell.")
+]
+
+
+def _fmt_options():
+    max_line_len = 60
+    max_name_len = max(len(o[0]) for o in OPTIONS)
+
+    # TODO Pad will be off if there are more than 9 options.
+    pad = ' ' * (max_name_len+6)
+
+    msg = []
+    for i, (name, _, desc) in enumerate(OPTIONS):
+        desc = wrap(desc, width=max_line_len)
+        desc = [desc[0]] + [pad + l for l in desc[1:]]
+
+        optstr = '{}) {} - {}\n'.format(
+            i+1, name.ljust(max_name_len), '\n'.join(desc))
+        msg.append(optstr)
+    msg.append("Select one of the above options: ")
+    return '\n'.join(msg)
+
+
+def wizard():
+    print("This wizard can help you get started with some common debugging "
+          "workflows.\nWhat would you like to do?\n")
+    print(_fmt_options(), end="")
+    choice = None
+    while True:
+        choice = raw_input().decode('utf8')
+        try:
+            choice = int(choice)-1
+            if 0 <= choice < len(OPTIONS):
+                break
+        except ValueError:
+            pass
+
+        print("Must provide an integer from 1-{}:".format(len(OPTIONS)))
+
+    func = OPTIONS[choice][1]
+    ret = func()
+
+    print("Use the 'run-wizard' command to start this wizard again.")
+    return ret
+
+
+if __name__ == '__main__':
+    sys.exit(wizard())
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/bin/test.sh
@@ -0,0 +1,38 @@
+#! /bin/bash -vex
+
+set -x -e
+
+: GECKO_HEAD_REPOSITORY         ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
+: GECKO_HEAD_REV                ${GECKO_HEAD_REV:=default}
+: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
+
+
+# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
+# to a simple fail-if-root check
+if [ $(id -u) = 0 ]; then
+    chown -R worker:worker /home/worker
+    # drop privileges by re-running this script
+    exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
+fi
+
+fail() {
+    echo # make sure error message is on a new line
+    echo "[test.sh:error]" "${@}"
+    exit 1
+}
+
+####
+# Now get the test-linux.sh/test-ubuntu.sh script from the given Gecko tree
+# and run it with the same arguments.
+####
+
+[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
+cd $WORKSPACE
+
+script=taskcluster/scripts/tester/test-ubuntu1604.sh
+url=${GECKO_HEAD_REPOSITORY}/raw-file/${GECKO_HEAD_REV}/${script}
+if ! curl --fail -o ./test-linux.sh --retry 10 $url; then
+    fail "failed downloading test-ubuntu1604.sh from ${GECKO_HEAD_REPOSITORY}"
+fi
+chmod +x ./test-linux.sh
+exec ./test-linux.sh "${@}"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/buildprops.json
@@ -0,0 +1,8 @@
+{
+  "properties": {
+    "buildername": ""
+  },
+  "sourcestamp": {
+    "changes": []
+  }
+}
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/deja-dup-monitor.desktop
@@ -0,0 +1,19 @@
+[Desktop Entry]
+Version=1.0
+X-Ubuntu-Gettext-Domain=deja-dup
+
+Name=Backup Monitor
+Comment=Schedules backups at regular intervals
+
+Icon=deja-dup
+TryExec=/usr/lib/deja-dup/deja-dup/deja-dup-monitor
+Exec=/usr/lib/deja-dup/deja-dup/deja-dup-monitor
+
+# Bug 984944/1240084 - It prevents taking screenshots
+X-GNOME-Autostart-Delay=false
+
+StartupNotify=false
+NoDisplay=true
+
+Type=Application
+Categories=System;Utility;Archiving;
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/pip/pip.conf
@@ -0,0 +1,4 @@
+[global]
+disable-pip-version-check = true
+trusted-host = pypi.pub.build.mozilla.org
+
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+
+XDG_DESKTOP_DIR="$HOME/Desktop"
+XDG_DOWNLOAD_DIR="$HOME/Downloads"
+XDG_TEMPLATES_DIR="$HOME/Templates"
+XDG_PUBLICSHARE_DIR="$HOME/Public"
+XDG_DOCUMENTS_DIR="$HOME/Documents"
+XDG_MUSIC_DIR="$HOME/Music"
+XDG_PICTURES_DIR="$HOME/Pictures"
+XDG_VIDEOS_DIR="$HOME/Videos"
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/config/user-dirs.locale
@@ -0,0 +1,1 @@
+en_US
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/hgrc
@@ -0,0 +1,12 @@
+[diff]
+showfunc = 1
+unified = 8
+
+[extensions]
+color =
+pager =
+progress =
+rebase =
+
+[web]
+cacerts = /etc/ssl/certs/ca-certificates.crt
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/dot-files/pulse/default.pa
@@ -0,0 +1,164 @@
+#!/usr/bin/pulseaudio -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+# This startup script is used only if PulseAudio is started per-user
+# (i.e. not in system mode)
+
+.nofail
+
+### Load something into the sample cache
+#load-sample-lazy x11-bell /usr/share/sounds/gtk-events/activate.wav
+#load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-coldplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-access /usr/share/sounds/generic.wav
+
+.fail
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+load-module module-card-restore
+
+### Automatically augment property information from .desktop files
+### stored in /usr/share/application
+load-module module-augment-properties
+
+### Load audio drivers statically
+### (it's probably better to not load these drivers manually, but instead
+### use module-udev-detect -- see below -- for doing this automatically)
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#load-module module-null-sink
+#load-module module-pipe-sink
+
+### Automatically load driver modules depending on the hardware available
+.ifexists module-udev-detect.so
+load-module module-udev-detect
+.else
+### Use the static hardware detection module (for systems that lack udev/hal support)
+load-module module-detect
+.endif
+
+### Automatically connect sink and source if JACK server is present
+.ifexists module-jackdbus-detect.so
+.nofail
+load-module module-jackdbus-detect
+.fail
+.endif
+
+### Automatically load driver modules for Bluetooth hardware
+# This module causes a pulseaudio startup failure on "gecko-tester"
+#.ifexists module-bluetooth-discover.so
+#load-module module-bluetooth-discover
+#.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix.so
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+#load-module module-zeroconf-publish
+
+### Load the RTP receiver module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
+#load-module module-rtp-send source=rtp.monitor
+
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf.so
+.nofail
+load-module module-gconf
+.fail
+.endif
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Automatically move streams to the default sink if the sink they are
+### connected to dies, similar for sources
+load-module module-rescue-streams
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Honour intended role device property
+load-module module-intended-roles
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+# This module causes a pulseaudio startup failure on "gecko-tester"
+#.ifexists module-console-kit.so
+#load-module module-console-kit
+#.endif
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Cork music streams when a phone stream is active
+#load-module module-cork-music-on-phone
+
+### Modules to allow autoloading of filters (such as echo cancellation)
+### on demand. module-filter-heuristics tries to determine what filters
+### make sense, and module-filter-apply does the heavy-lifting of
+### loading modules and rerouting streams.
+load-module module-filter-heuristics
+load-module module-filter-apply
+
+### Load DBus protocol
+#.ifexists module-dbus-protocol.so
+#load-module module-dbus-protocol
+#.endif
+
+# X11 modules should not be started from default.pa so that one daemon
+# can be shared by multiple sessions.
+
+### Load X11 bell module
+#load-module module-x11-bell sample=bell-windowing-system
+
+### Register ourselves in the X11 session manager
+#load-module module-x11-xsmp
+
+### Publish connection data in the X11 root window
+#.ifexists module-x11-publish.so
+#.nofail
+#load-module module-x11-publish
+#.fail
+#.endif
+
+load-module module-switch-on-port-available
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/fonts.conf
@@ -0,0 +1,5 @@
+<match target="font">
+  <edit name="antialias" mode="assign">
+   <bool>false</bool>
+  </edit>
+</match>
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/jockey-gtk.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Name=Check for new hardware drivers
+Comment=Notify about new hardware drivers available for the system
+Icon=jockey
+Exec=sh -c "test -e /var/cache/jockey/check || exec jockey-gtk --check"
+Terminal=false
+Type=Application
+Categories=System;Settings;GTK;HardwareSettings;
+NotShowIn=KDE;
+X-Ubuntu-Gettext-Domain=jockey
+
+# Bug 984944/1240084 - It prevents taking screenshots
+X-GNOME-Autostart-Delay=false
+
+NoDisplay=true
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/motd
@@ -0,0 +1,6 @@
+Welcome to your taskcluster interactive shell! The regularly scheduled task
+has been paused to give you a chance to set up your debugging environment.
+
+For your convenience, the exact mozharness command needed for this task can
+be invoked using the 'run-mozharness' command.
+
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/release-upgrades
@@ -0,0 +1,17 @@
+# Default behavior for the release upgrader.
+
+[DEFAULT]
+# Default prompting behavior, valid options:
+#
+#  never  - Never check for a new release.
+#  normal - Check to see if a new release is available.  If more than one new
+#           release is found, the release upgrader will attempt to upgrade to
+#           the release that immediately succeeds the currently-running
+#           release.
+#  lts    - Check to see if a new LTS release is available.  The upgrader
+#           will attempt to upgrade to the first LTS release available after
+#           the currently-running one.  Note that this option should not be
+#           used if the currently-running release is not itself an LTS
+#           release, since in that case the upgrader won't be able to
+#           determine if a newer release is available.
+Prompt=never
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/taskcluster-interactive-shell
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+/home/worker/bin/run-wizard;
+
+SPAWN="$SHELL";
+
+if [ "$SHELL" = "bash" ]; then
+  SPAWN="bash -li";
+fi;
+
+exec $SPAWN;
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/tc-vcs-config.yml
@@ -0,0 +1,40 @@
+# Default configuration used by the tc-vs tools these can be overridden by
+# passing the config you wish to use over the command line...
+git: git
+hg: hg
+
+repoCache:
+  # Repo url to clone when running repo init..
+  repoUrl: https://git.mozilla.org/external/google/gerrit/git-repo.git
+  # Version of repo to utilize...
+  repoRevision: master
+  # The root where all downloaded cache files are stored on the local machine...
+  cacheDir: '{{env.HOME}}/.tc-vcs-repo/'
+  # Name/prefixed used as part of the base url.
+  cacheName: sources/{{name}}.tar.gz
+  # Command used to upload the tarball
+  uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
+  # Large http get requests are often slower using nodes built in http layer so
+  # we utilize a subprocess which is responsible for fetching...
+  get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
+  # Used to create clone tarball
+  compress: tar -czf {{dest}} {{source}}
+  # All cache urls use tar + gz this is the command used to extract those files
+  # downloaded by the "get" command.
+  extract: tar -x -z -C {{dest}} -f {{source}}
+
+cloneCache:
+  # The root where all downloaded cache files are stored on the local machine...
+  cacheDir: '{{env.HOME}}/.tc-vcs/'
+  # Command used to upload the tarball
+  uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
+  # Large http get requests are often slower using nodes built in http layer so
+  # we utilize a subprocess which is responsible for fetching...
+  get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
+  # Used to create clone tarball
+  compress: tar -czf {{dest}} {{source}}
+  # All cache urls use tar + gz this is the command used to extract those files
+  # downloaded by the "get" command.
+  extract: tar -x -z --strip-components 1 -C {{dest}} -f {{source}}
+  # Name/prefixed used as part of the base url.
+  cacheName: clones/{{name}}.tar.gz
new file mode 100644
--- /dev/null
+++ b/testing/docker/desktop1604-test/tester.env
@@ -0,0 +1,4 @@
+GAIA_REV=tip
+GAIA_REF=tip
+GAIA_BASE_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
+GAIA_HEAD_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/Dockerfile
@@ -0,0 +1,22 @@
+FROM          ubuntu
+MAINTAINER    Joel Maher <joel.maher@gmail.com>
+
+RUN useradd -d /home/worker -s /bin/bash -m worker
+WORKDIR /home/worker
+
+# install non-build specific dependencies in a single layer
+ADD           system-setup.sh   /tmp/system-setup.sh
+RUN           bash /tmp/system-setup.sh
+
+# Set variable normally configured at login, by the shells parent process, these
+# are taken from GNU su manual
+ENV           HOME          /home/worker
+ENV           SHELL         /bin/bash
+ENV           USER          worker
+ENV           LOGNAME       worker
+ENV           HOSTNAME      taskcluster-worker
+ENV           LANG          en_US.UTF-8
+ENV           LC_ALL        en_US.UTF-8
+
+# Set a default command useful for debugging
+CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/REGISTRY
@@ -0,0 +1,1 @@
+taskcluster
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/VERSION
@@ -0,0 +1,1 @@
+0.1.3
new file mode 100644
--- /dev/null
+++ b/testing/docker/ubuntu1604-test/system-setup.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env bash
+
+set -ve
+
+test `whoami` == 'root'
+
+mkdir -p /setup
+cd /setup
+
+apt_packages=()
+
+apt_packages+=('alsa-base')
+apt_packages+=('alsa-utils')
+apt_packages+=('autoconf2.13')
+apt_packages+=('bluez-cups')
+apt_packages+=('build-essential')
+apt_packages+=('ca-certificates')
+apt_packages+=('ccache')
+apt_packages+=('curl')
+apt_packages+=('fonts-kacst')
+apt_packages+=('fonts-kacst-one')
+apt_packages+=('fonts-liberation')
+apt_packages+=('fonts-stix')
+apt_packages+=('fonts-unfonts-core')
+apt_packages+=('fonts-unfonts-extra')
+apt_packages+=('fonts-vlgothic')
+apt_packages+=('g++-multilib')
+apt_packages+=('gcc-multilib')
+apt_packages+=('gir1.2-gnomebluetooth-1.0')
+apt_packages+=('git')
+apt_packages+=('gstreamer0.10-alsa')
+apt_packages+=('gstreamer0.10-plugins-base')
+apt_packages+=('gstreamer0.10-plugins-good')
+apt_packages+=('gstreamer0.10-tools')
+apt_packages+=('language-pack-en-base')
+apt_packages+=('libasound2-dev')
+apt_packages+=('libcanberra-pulse')
+apt_packages+=('libcurl4-openssl-dev')
+apt_packages+=('libdbus-1-dev')
+apt_packages+=('libdbus-glib-1-dev')
+apt_packages+=('libgconf2-dev')
+apt_packages+=('libgstreamer-plugins-base0.10-dev')
+apt_packages+=('libgstreamer0.10-dev')
+apt_packages+=('libgtk2.0-dev')
+apt_packages+=('libiw-dev')
+apt_packages+=('libnotify-dev')
+apt_packages+=('libpulse-dev')
+apt_packages+=('libsox-fmt-alsa')
+apt_packages+=('libxt-dev')
+apt_packages+=('libxxf86vm1')
+apt_packages+=('llvm')
+apt_packages+=('llvm-dev')
+apt_packages+=('llvm-runtime')
+apt_packages+=('nano')
+apt_packages+=('pulseaudio')
+apt_packages+=('pulseaudio-module-bluetooth')
+apt_packages+=('pulseaudio-module-gconf')
+apt_packages+=('rlwrap')
+apt_packages+=('screen')
+apt_packages+=('software-properties-common')
+apt_packages+=('sudo')
+apt_packages+=('tar')
+apt_packages+=('ttf-dejavu')
+apt_packages+=('ubuntu-desktop')
+apt_packages+=('unzip')
+apt_packages+=('uuid')
+apt_packages+=('vim')
+apt_packages+=('wget')
+apt_packages+=('xvfb')
+apt_packages+=('yasm')
+apt_packages+=('zip')
+
+# get xvinfo for test-linux.sh to monitor Xvfb startup
+apt_packages+=('x11-utils')
+
+# Bug 1232407 - this allows the user to start vnc
+apt_packages+=('x11vnc')
+
+# Bug 1176031: need `xset` to disable screensavers
+apt_packages+=('x11-xserver-utils')
+
+# use Ubuntu's Python-2.7 (2.7.3 on Precise)
+apt_packages+=('python-dev')
+apt_packages+=('python-pip')
+
+apt-get update
+# This allows ubuntu-desktop to be installed without human interaction
+export DEBIAN_FRONTEND=noninteractive
+apt-get install -y -f ${apt_packages[@]}
+
+dpkg-reconfigure locales
+
+# set up tooltool (temporarily)
+curl https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py > /setup/tooltool.py
+tooltool_fetch() {
+    cat >manifest.tt
+    python /setup/tooltool.py fetch
+    rm manifest.tt
+}
+
+pip install --upgrade pip
+
+pip install virtualenv
+pip install mercurial
+
+# Install node
+tooltool_fetch <<'EOF'
+[
+{
+    "size": 5676610,
+    "digest": "ce27b788dfd141a5ba7674332825fc136fe2c4f49a319dd19b3a87c8fffa7a97d86cbb8535661c9a68c9122719aa969fc6a8c886458a0df9fc822eec99ed130b",
+    "algorithm": "sha512",
+    "filename": "node-v0.10.36-linux-x64.tar.gz"
+}
+]
+
+EOF
+tar -C /usr/local -xz --strip-components 1 < node-*.tar.gz
+node -v  # verify
+
+# Install custom-built Debian packages.  These come from a set of repositories
+# packaged in tarballs on tooltool to make them replicable.  Because they have
+# inter-dependenices, we install all repositories first, then perform the
+# installation.
+cp /etc/apt/sources.list sources.list.orig
+
+# Install Valgrind (trunk, late Jan 2016) and do some crude sanity
+# checks.  It has to go in /usr/local, otherwise it won't work.  Copy
+# the launcher binary to /usr/bin, though, so that direct invokations
+# of /usr/bin/valgrind also work.  Also install libc6-dbg since
+# Valgrind won't work at all without the debug symbols for libc.so and
+# ld.so being available.
+tooltool_fetch <<'EOF'
+[
+{
+    "size": 41331092,
+    "visibility": "public",
+    "digest": "a89393c39171b8304fc262094a650df9a756543ffe9fbec935911e7b86842c4828b9b831698f97612abb0eca95cf7f7b3ff33ea7a9b0313b30c9be413a5efffc",
+    "algorithm": "sha512",
+    "filename": "valgrind-15775-3206-ubuntu1204.tgz"
+}
+]
+EOF
+cp valgrind-15775-3206-ubuntu1204.tgz /tmp
+(cd / && tar xzf /tmp/valgrind-15775-3206-ubuntu1204.tgz)
+rm /tmp/valgrind-15775-3206-ubuntu1204.tgz
+cp /usr/local/bin/valgrind /usr/bin/valgrind
+apt-get install -y libc6-dbg
+valgrind --version
+valgrind date
+
+# adding multiverse to get 'ubuntu-restricted-extras' below
+apt-add-repository multiverse
+apt-get update
+
+# for mp4 codec (used in MSE tests)
+apt-get -q -y -f install ubuntu-restricted-extras
+
+apt-get -q -y -f install \
+    libxcb1 \
+    libxcb-render0 \
+    libxcb-shm0 \
+    libxcb-glx0 \
+    libxcb-shape0
+libxcb1_version=$(dpkg-query -s libxcb1 | grep ^Version | awk '{ print $2 }')
+[ "$libxcb1_version" = "1.11.1-1ubuntu1" ] || exit 1
+
+apt-get -q -y -f install \
+    libgl1-mesa-dri \
+    libgl1-mesa-glx \
+    mesa-common-dev
+mesa_version=$(dpkg-query -s libgl1-mesa-dri | grep ^Version | awk '{ print $2 }')
+echo $mesa_version
+[ "$mesa_version" = "11.2.0-1ubuntu2" ] || exit 1
+
+# revert the list of repos
+cp sources.list.orig /etc/apt/sources.list
+apt-get update
+
+# clean up
+cd /
+rm -rf /setup ~/.ccache ~/.cache ~/.npm
+apt-get clean
+apt-get autoclean
+rm $0