Bug 1142779 - Enable testdroid devices to be used within test tasks r=lightsofapollo
authorGregory Arndt <garndt@mozilla.com>
Thu, 12 Mar 2015 17:43:28 -0500
changeset 235693 3d8168ff207292ea48558174e6f7ac99be035adf
parent 235692 30236ca212cb40e005df12f2b989d80402d5b3e1
child 235694 06367ce03cce6d9c11aa0910ddd11838e36da108
push id14740
push usergarndt@mozilla.com
push dateThu, 26 Mar 2015 12:57:38 +0000
treeherderb2g-inbound@e6f2dc7de4bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslightsofapollo
bugs1142779
milestone39.0a1
Bug 1142779 - Enable testdroid devices to be used within test tasks r=lightsofapollo
testing/docker/tester-device/Dockerfile
testing/docker/tester-device/VERSION
testing/docker/tester-device/bin/entrypoint
testing/docker/tester-device/bin/validate_task.py
testing/docker/tester-device/build.sh
testing/docker/tester-device/tests/invalid_base_repo.yml
testing/docker/tester-device/tests/invalid_build.yml
testing/docker/tester-device/tests/invalid_head_repo.yml
testing/docker/tester-device/tests/public.yml
testing/docker/tester-device/tests/test_validation.py
testing/docker/tester-device/tests/valid.yml
testing/taskcluster/mach_commands.py
testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
testing/taskcluster/tasks/branches/base_job_flags.yml
testing/taskcluster/tasks/builds/b2g_flame_kk_eng.yml
testing/taskcluster/tasks/phone_test.yml
testing/taskcluster/tasks/tests/flame_kk_gaia_ui_test_sanity.yml
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/Dockerfile
@@ -0,0 +1,48 @@
+FROM        ubuntu:14.04
+MAINTAINER  Greg Arndt <garndt@mozilla.com>
+
+RUN mkdir -p /home/worker/upload/logs
+
+WORKDIR /home/worker
+
+RUN apt-get install -y curl
+
+# Add PPA for latest nodejs versions. Do not need to run apt-get update after this
+# as the script already does it.
+RUN curl -sL https://deb.nodesource.com/setup | sudo bash -
+
+RUN apt-get upgrade -y && apt-get install -y \
+  build-essential \
+  ca-certificates \
+  nodejs \
+  python-dev \
+  mercurial \
+  git \
+  android-tools-adb \
+  android-tools-fastboot\
+  jq
+
+# Get pip and virtualenv
+RUN curl https://bootstrap.pypa.io/get-pip.py | python
+RUN pip install virtualenv
+
+RUN git config --global user.email "mozilla@example.com" && \
+    git config --global user.name "mozilla"
+
+
+# Get node packages
+RUN npm install -g taskcluster-vcs@2.3.0
+
+WORKDIR /home/worker
+
+ADD bin /home/worker/bin
+ADD data /home/worker/data
+ADD https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
+RUN chmod u+x /home/worker/bin/*
+
+ENV HOME /home/worker
+ENV SHELL /bin/bash
+ENV PATH $PATH:/home/worker/bin
+ENV CLOUD_HOST testdroid
+
+ENTRYPOINT ["entrypoint"]
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/VERSION
@@ -0,0 +1,1 @@
+0.0.2
new file mode 100755
--- /dev/null
+++ b/testing/docker/tester-device/bin/entrypoint
@@ -0,0 +1,26 @@
+#! /bin/bash -e
+
+echo "Validating Task"
+python /home/worker/bin/validate_task.py
+
+echo "Retrieving device"
+res=`curl --request POST -H "Content-Type: application/json" -d "$DEVICE_CAPABILITIES" http://$CLOUD_HOST/device`
+status=`echo $res | jq .session`
+
+if [[ $status == 'null' ]]; then
+    echo "Session could not be created with a device."
+    exit -1
+fi
+
+export SESSION_ID=`echo $res | jq .session.id`
+export SERIAL_ID=`echo $res | jq -r .proxies.adb.serialId`
+export ADB_HOST=`echo $res | jq .proxies.adb.forwardHost`
+export ADB_PORT=`echo $res | jq .proxies.adb.port`
+export MARIONETTE_HOST=`echo $res | jq .proxies.marionette.forwardHost`
+export MARIONETTE_PORT=`echo $res | jq .proxies.marionette.port`
+export PROXY_HOST=`echo $res | jq -r .proxyHost`
+echo "Retrieved device.  Session: $SESSION_ID"
+
+curl -o /home/worker/data/device.json -s -H "Accept: application/json" http://$CLOUD_HOST/device/properties
+
+eval $@
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/bin/validate_task.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import os
+import os.path
+import json
+import urllib2
+import sys
+import re
+import subprocess
+
+repo_matcher = re.compile(r'[a-z]+://(hg|git)\.mozilla\.org')
+image_matcher = re.compile(r'^https:\/\/queue\.taskcluster\.net\/v1\/task\/.+\/artifacts\/private\/build\/flame-kk\.zip$')
+
+def get_task(taskid):
+    return json.load(urllib2.urlopen('https://queue.taskcluster.net/v1/task/' + taskid))
+
+def check_task(task):
+    payload = task['payload']
+
+    if 'DEVICE_CAPABILITIES' not in payload['env']:
+        print('Device capalities are required.', file=sys.stderr)
+        return -1
+
+    capabilities = json.loads(payload['env']['DEVICE_CAPABILITIES'])
+
+    if 'build' not in capabilities:
+        print('Build image url is required', file=sys.stderr)
+        return -1
+
+    image = capabilities['build']
+
+    if not image_matcher.match(image):
+        print('Invalid image url', file=sys.stderr)
+        return -1
+
+    if 'GAIA_HEAD_REPOSITORY' not in payload['env']:
+        print('Task has no head gaia repository', file=sys.stderr)
+        return -1
+
+    repo = payload['env']['GAIA_HEAD_REPOSITORY']
+    # if it is not a mozilla repository, fail
+    if not repo_matcher.match(repo):
+        print('Invalid head repository', repo, file=sys.stderr)
+        return -1
+
+    if 'GAIA_BASE_REPOSITORY' not in payload['env']:
+        print('Task has no base gaia repository', file=sys.stderr)
+        return -1
+
+    repo = payload['env']['GAIA_BASE_REPOSITORY']
+    if not repo_matcher.match(repo):
+        print('Invalid base repository', repo, file=sys.stderr)
+        return -1
+
+    if 'artifacts' in payload:
+        artifacts = payload['artifacts']
+        # If any of the artifacts makes reference to 'public',
+        # abort the task
+        if any(map(lambda a: 'public' in a, artifacts)):
+            print('Cannot upload to public', file=sys.stderr)
+            return -1
+
+    return 0
+
+def main():
+    taskid = os.getenv('TASK_ID')
+
+    # If the task id is None, we assume we are running docker locally
+    if taskid is not None:
+        task = get_task(taskid)
+        sys.exit(check_task(task))
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/testing/docker/tester-device/build.sh
@@ -0,0 +1,22 @@
+#! /bin/bash -ve
+
+while getopts "t:g:" arg; do
+  case $arg in
+    t)
+      TAG=$OPTARG
+      ;;
+    g)
+      GAIA_TESTVARS=$OPTARG
+      ;;
+  esac
+done
+
+pushd $(dirname $0)
+
+test $TAG
+test -f "$GAIA_TESTVARS"
+
+cp $GAIA_TESTVARS data/gaia_testvars.json
+
+docker build -t $TAG .
+rm -f data/gaia_testvars.json
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/tests/invalid_base_repo.yml
@@ -0,0 +1,29 @@
+taskId: 1
+task:
+  metadata:
+    name: '[TC] Gaia Python Integration Tests - device'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: '{"type":"flame","memory":"319","sims": "1","build":"https://queue.taskcluster.net/v1/task/H0FPqxakT06Eg4wo4zPwMw/runs/0/artifacts/private/build/flame-kk.zip"}'
+      GAIA_HEAD_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+      GAIA_BASE_REPOSITORY: 'http://github.com/mozilla-b2g/gaia'
+    command:
+      - entrypoint
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/testdroid.json --testvars=/home/worker/data/acknowledge_risks.json --testvars=/home/worker/data/common.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'private/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/tests/invalid_build.yml
@@ -0,0 +1,29 @@
+taskId: 1
+task:
+  metadata:
+    name: '[TC] Gaia Python Integration Tests - device'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: "{\"type\":\"flame\",\"memory\":\"319\",\"sims\": \"1\",\"build\":\"https://queue.some_other_domain.net/v1/task/fLOiHaudRkepg9yEiaM1Mg/artifacts/private/build/flame-kk.zip\"}"
+      GAIA_HEAD_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+      GAIA_BASE_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+    command:
+      - entrypoint
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/testdroid.json --testvars=/home/worker/data/acknowledge_risks.json --testvars=/home/worker/data/common.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'private/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/tests/invalid_head_repo.yml
@@ -0,0 +1,29 @@
+taskId: 1
+task:
+  metadata:
+    name: '[TC] Gaia Python Integration Tests - device'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: '{"type":"flame","memory":"319","sims": "1","build":"https://queue.taskcluster.net/v1/task/H0FPqxakT06Eg4wo4zPwMw/runs/0/artifacts/private/build/flame-kk.zip"}'
+      GAIA_BASE_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+      GAIA_HEAD_REPOSITORY: 'http://github.com/mozilla-b2g/gaia'
+    command:
+      - entrypoint
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/testdroid.json --testvars=/home/worker/data/acknowledge_risks.json --testvars=/home/worker/data/common.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'private/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/tests/public.yml
@@ -0,0 +1,29 @@
+taskId: 1
+task:
+  metadata:
+    name: '[TC] Gaia Python Integration Tests - device'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: '{"type":"flame","memory":"319","sims": "1","build":"https://queue.taskcluster.net/v1/task/H0FPqxakT06Eg4wo4zPwMw/runs/0/artifacts/private/build/flame-kk.zip"}'
+      GAIA_BASE_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+      GAIA_HEAD_REPOSITORY: 'http://github.com/mozilla-b2g/gaia'
+    command:
+      - entrypoint
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/testdroid.json --testvars=/home/worker/data/acknowledge_risks.json --testvars=/home/worker/data/common.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'public/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
new file mode 100755
--- /dev/null
+++ b/testing/docker/tester-device/tests/test_validation.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import unittest
+import sys
+import yaml
+sys.path.append('../bin')
+from validate_task import check_task
+
+def load_task(task_file):
+    content = open(task_file, 'r')
+    return yaml.load(content)['task']
+
+class TaskValidationTest(unittest.TestCase):
+    def test_valid_task(self):
+        task = load_task('valid.yml')
+        self.assertEquals(check_task(task), 0)
+
+    def test_invalid_base_repo(self):
+        task = load_task('invalid_base_repo.yml')
+        self.assertEquals(check_task(task), -1)
+
+    def test_invalid_head_repo(self):
+        task = load_task('invalid_head_repo.yml')
+        self.assertEquals(check_task(task), -1)
+
+    def test_public_artifact(self):
+        task = load_task('public.yml')
+        self.assertEquals(check_task(task), -1)
+
+    def test_invalid_build(self):
+        task = load_task('invalid_build.yml')
+        self.assertEquals(check_task(task), -1)
+
+if __name__ == '__main__':
+    unittest.main()
new file mode 100644
--- /dev/null
+++ b/testing/docker/tester-device/tests/valid.yml
@@ -0,0 +1,29 @@
+taskId: 1
+task:
+  metadata:
+    name: '[TC] Gaia Python Integration Tests - device'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: "{\"type\":\"flame\",\"memory\":\"319\",\"sims\": \"1\",\"build\":\"https://queue.taskcluster.net/v1/task/fLOiHaudRkepg9yEiaM1Mg/artifacts/private/build/flame-kk.zip\"}"
+      GAIA_HEAD_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+      GAIA_BASE_REPOSITORY: 'http://hg.mozilla.org/integration/gaia-central'
+    command:
+      - entrypoint
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/testdroid.json --testvars=/home/worker/data/acknowledge_risks.json --testvars=/home/worker/data/common.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'private/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
--- a/testing/taskcluster/mach_commands.py
+++ b/testing/taskcluster/mach_commands.py
@@ -306,16 +306,22 @@ class Graph(object):
                 build_task['task']['extra']['locations']['tests']
             )
 
             build_url = ARTIFACT_URL.format(
                 build_parameters['build_slugid'],
                 build_task['task']['extra']['locations']['build']
             )
 
+            # img_url is only necessary for device builds
+            img_url = ARTIFACT_URL.format(
+                build_parameters['build_slugid'],
+                build_task['task']['extra']['locations'].get('img', '')
+            )
+
             define_task = DEFINE_TASK.format(build_task['task']['workerType'])
 
             graph['scopes'].append(define_task)
             graph['scopes'].extend(build_task['task'].get('scopes', []))
 
             # Treeherder symbol configuration for the graph required for each
             # build so tests know which platform they belong to.
             build_treeherder_config = build_task['task']['extra']['treeherder']
@@ -334,16 +340,17 @@ class Graph(object):
             if len(build_treeherder_config['collection'].keys()) != 1:
                 message = '({}), extra.treeherder.collection must contain one type'
                 raise ValueError(message.fomrat(build['task']))
 
             for test in build['dependents']:
                 test = test['allowed_build_tasks'][build['task']]
                 test_parameters = copy.copy(build_parameters)
                 test_parameters['build_url'] = build_url
+                test_parameters['img_url'] = img_url
                 test_parameters['tests_url'] = tests_url
 
                 test_definition = templates.load(test['task'], {})['task']
                 chunk_config = test_definition['extra']['chunks']
 
                 # Allow branch configs to override task level chunking...
                 if 'chunks' in test:
                     chunk_config['total'] = test['chunks']
@@ -444,29 +451,29 @@ class CIBuild(object):
             head_repository = get_hg_url()
 
         head_rev = params['head_rev']
         if not head_rev:
             head_rev = get_latest_hg_revision(head_repository)
 
         head_ref = params['head_ref'] or head_rev
 
-        build_parameters = {
+        build_parameters = dict(gaia_info().items() + {
             'docker_image': docker_image,
             'owner': params['owner'],
             'from_now': json_time_from_now,
             'now': current_json_time(),
             'base_repository': params['base_repository'] or head_repository,
             'head_repository': head_repository,
             'head_rev': head_rev,
             'head_ref': head_ref,
             'mozharness_repository': params['mozharness_repository'],
             'mozharness_ref': params['mozharness_ref'],
             'mozharness_rev': params['mozharness_rev']
-        }
+        }.items())
 
         try:
             build_task = templates.load(params['build_task'], build_parameters)
         except IOError:
             sys.stderr.write(
                 "Could not load build task file.  Ensure path is a relative " \
                 "path from testing/taskcluster"
             )
@@ -496,44 +503,46 @@ class CITest(object):
 
         if chunk is None:
             chunk = 1
 
         if chunk < 1 or chunk > total_chunks:
             raise ValueError(
                 '"chunk" must be a value between 1 and "total_chunks (default 1)"')
 
-        build_url, tests_url = self._get_build_and_tests_url(task_id)
+        build_url, img_url, tests_url = self._get_build_and_tests_url(task_id)
 
-        test_parameters = {
+        test_parameters = dict(gaia_info().items() + {
             'docker_image': docker_image,
             'build_url': ARTIFACT_URL.format(task_id, build_url),
+            'img_url': ARTIFACT_URL.format(task_id, img_url),
             'tests_url': ARTIFACT_URL.format(task_id, tests_url),
             'total_chunks': total_chunks,
             'chunk': chunk,
             'owner': owner,
             'from_now': json_time_from_now,
             'now': current_json_time()
-        }
+        }.items())
 
         try:
-            test_task = import_yaml(test_task, test_parameters)
+            templates = Templates(ROOT)
+            test_task = templates.load(test_task, test_parameters)
         except IOError:
             sys.stderr.write(
                 "Could not load test task file.  Ensure path is a relative " \
                 "path from testing/taskcluster"
             )
             sys.exit(1)
 
         print(json.dumps(test_task['task'], indent=4))
 
     def _get_build_and_tests_url(self, task_id):
         task = get_task(task_id)
         locations = task['extra']['locations']
-        return locations['build'], locations['tests']
+        return locations['build'], locations.get('img', ''), locations['tests']
 
 @CommandProvider
 class CIDockerRun(object):
     @Command('taskcluster-docker-run', category='ci',
         description='Run a docker image and optionally mount local hg repos. ' \
                     'Repos will be mounted to /home/worker/x/source accordingly. ' \
                     'For example, to run a centos image and mount local gecko ' \
                     'and gaia repos: mach ci-docker-run --local-gecko-repo ' \
--- a/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
@@ -13,8 +13,11 @@ builds:
       opt:
         task: tasks/builds/b2g_flame_kk_opt.yml
   flame-kk-eng:
     platforms:
       - b2g
     types:
       opt:
         task: tasks/builds/b2g_flame_kk_eng.yml
+
+  gaia-ui-test-sanity:
+        task: tasks/tests/flame_kk_gaia_ui_test_sanity.yml
\ No newline at end of file
--- a/testing/taskcluster/tasks/branches/base_job_flags.yml
+++ b/testing/taskcluster/tasks/branches/base_job_flags.yml
@@ -23,16 +23,17 @@ flags:
     - crashtest
     - crashtest-ipc
     - gaia-build
     - gaia-build-unit
     - gaia-js-integration
     - gaia-linter
     - gaia-unit
     - gaia-unit-oop
+    - gaia-ui-test-sanity
     - gaia-ui-test-oop
     - gaia-ui-test-accessibility
     - gaia-ui-test-functional
     - gaia-ui-test-unit
     - jetpack
     - jittests
     - jsreftest
     - marionette
--- a/testing/taskcluster/tasks/builds/b2g_flame_kk_eng.yml
+++ b/testing/taskcluster/tasks/builds/b2g_flame_kk_eng.yml
@@ -24,9 +24,12 @@ task:
       symbol: Be
       groupSymbol: Flame-KK
       groupName: Flame KitKat Device Image
       machine:
         platform: b2g-device-image
     locations:
       img: 'private/build/flame-kk.zip'
 
+      GAIA_OPTIMIZE: '1'
+      B2G_SYSTEM_APPS: '1'
+      B2G_UPDATER: '1'
 
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/phone_test.yml
@@ -0,0 +1,42 @@
+# This task is the base for most tests in gecko.
+task:
+  created: '{{now}}'
+  deadline: '{{#from_now}}24 hours{{/from_now}}'
+  metadata:
+    source: http://todo.com/soon
+    owner: mozilla-taskcluster-maintenance@mozilla.com
+  tags:
+    createdForUser: {{owner}}
+  workerType: b2gtest
+  provisionerId: aws-provisioner
+  schedulerId: task-graph-scheduler
+
+  scopes:
+    - 'docker-worker:image:{{#docker_image}}tester-device{{/docker_image}}'
+    - 'queue:create-task:aws-provisioner/testdroid-device'
+    - 'docker-worker:cache:tc-vcs'
+
+  payload:
+    image: '{{#docker_image}}tester-device{{/docker_image}}'
+    maxRunTime: 3600
+    cache:
+      tc-vcs: '/home/worker/.tc-vcs'
+    env:
+      GAIA_HEAD_REPOSITORY: '{{{gaia_head_repository}}}'
+      GAIA_BASE_REPOSITORY: '{{{gaia_base_repository}}}'
+      GAIA_REF: '{{{gaia_ref}}}'
+      GAIA_REV: '{{{gaia_rev}}}'
+      MOZHARNESS_REPOSITORY: '{{mozharness_repository}}'
+      MOZHARNESS_REV: '{{mozharness_rev}}'
+
+    # All builds share a common artifact directory for ease of uploading.
+    artifacts:
+      'private/logs':
+        type: directory
+        path: '/home/worker/upload/logs/'
+        expires: '{{#from_now}}1 year{{/from_now}}'
+
+  extra:
+    treeherder:
+      groupSymbol: tc
+      groupName: Submitted by taskcluster
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/tests/flame_kk_gaia_ui_test_sanity.yml
@@ -0,0 +1,36 @@
+---
+$inherits:
+  from: 'tasks/phone_test.yml'
+task:
+  metadata:
+    name: '[TC] Gaia Python Sanity Integration Tests'
+    description: Gaia Python Integration Tests
+  workerType: testdroid-device
+  retries: 0
+
+  payload:
+    env:
+      DEVICE_CAPABILITIES: '{"type":"flame","memory":"319","sims": "1","build":"{{{img_url}}}"}'
+    features:
+      testdroidProxy: true
+    maxRunTime: 7200
+    command:
+      - >
+        tc-vcs checkout /home/worker/gaia/source $GAIA_BASE_REPOSITORY $GAIA_HEAD_REPOSITORY $GAIA_REV $GAIA_REF &&
+        cd gaia/source/tests/python/gaia-ui-tests/ &&
+        python setup.py develop &&
+        pip install py &&
+        cd /home/worker/ &&
+        gaiatest --testvars=/home/worker/data/gaia_testvars.json --testvars=/home/worker/data/device.json --adb-host=$PROXY_HOST --adb-port=$ADB_PORT --address=$PROXY_HOST:$MARIONETTE_PORT --device $SERIAL_ID --xml-output=/home/worker/upload/logs/xml_output.xml --timeout=10000 --log-html=/home/worker/upload/logs/index.html --restart --type=b2g+sanity-dsds --log-mach=-  --log-raw=/home/worker/upload/logs/raw.log gaia/source/tests/python/gaia-ui-tests/gaiatest/tests/functional/manifest.ini
+    artifacts:
+      'private/device.json':
+        type: file
+        path: '/home/worker/data/device.json'
+        expires: '{{#from_now}}1 year{{/from_now}}'
+
+  extra:
+    treeherder:
+      groupName: Gaia Python Integration Tests
+      groupSymbol: tc-Gip
+      symbol: 'S'
+      productName: b2g