<?xml version="1.0"?>
<rdf:RDF
	xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:foaf="http://xmlns.com/foaf/0.1/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns="http://purl.org/rss/1.0/"
>
<channel rdf:about="https://planet.mozilla.org/taskcluster/">
	<title>Planet Taskcluster</title>
	<link>https://planet.mozilla.org/taskcluster/</link>
	<description>Planet Taskcluster - https://planet.mozilla.org/taskcluster/</description>
	<atom:link rel="self" href="https://planet.mozilla.org/taskcluster/rss10.xml" type="application/rss+xml"/>

	<items>
		<rdf:Seq>
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2020/12/tc-db-part-3.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html" />
			<rdf:li rdf:resource="http://petemoore.github.io/spectrum/2020/08/24/zx-spectrum-plus-4.html" />
			<rdf:li rdf:resource="http://petemoore.github.io/general/2020/08/24/nt-rights-open-source-alternative.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2020/05/debugging-docker-connection-reset-by-peer.html" />
			<rdf:li rdf:resource="https://walac.github.io/booting-packet-images-qemu/" />
			<rdf:li rdf:resource="tag:blogger.com,1999:blog-2324292491075571582.post-1015693008508820138" />
			<rdf:li rdf:resource="tag:blogger.com,1999:blog-2324292491075571582.post-3628648541656521060" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/08/ci-admin.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/06/actions-as-hooks.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/05/shipped-and-hosted-software.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/05/design-of-task-graph-generation.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/02/make-the-first-move.html" />
			<rdf:li rdf:resource="http://code.v.igoro.us/posts/2018/01/r14y.html" />
			<rdf:li rdf:resource="https://ckousik.github.io/gsoc/2017/07/19/Final-Work-Product" />
			<rdf:li rdf:resource="https://ckousik.github.io/gsoc/2017/06/16/WebSocket-Multiplexer-Overview" />
			<rdf:li rdf:resource="https://ckousik.github.io/gsoc/2017/05/24/Stream-Metrics" />
			<rdf:li rdf:resource="https://ckousik.github.io/gsoc/2017/05/16/Stream-States-Part-1" />
			<rdf:li rdf:resource="https://ckousik.github.io/gsoc/2017/05/08/Webhook-Tunnel" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5379" />
			<rdf:li rdf:resource="https://walac.github.io/taskcluster-worker-macosx-engine/" />
			<rdf:li rdf:resource="tag:www.erranderr.com,2016-05-02:/blog/taskcluster-generic-tasks.html" />
			<rdf:li rdf:resource="https://walac.github.io/cors-proxy/" />
			<rdf:li rdf:resource="http://petemoore.github.io/general/taskcluster/2016/03/30/windows-sshd-cygwin-ec2-aws.html" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5365" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5355" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5357" />
			<rdf:li rdf:resource="https://jonasfj.dk/?p=481" />
			<rdf:li rdf:resource="tag:blogger.com,1999:blog-2324292491075571582.post-4384759603685314095" />
			<rdf:li rdf:resource="tag:www.erranderr.com,2016-02-09:/blog/taskcluster-learning.html" />
			<rdf:li rdf:resource="tag:blogger.com,1999:blog-2324292491075571582.post-1771023270396663807" />
			<rdf:li rdf:resource="https://walac.github.io/in-tree-config/" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5338" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5322" />
			<rdf:li rdf:resource="https://walac.github.io/taskcluster-b2g/" />
			<rdf:li rdf:resource="http://petemoore.github.io/general/taskcluster/2015/09/30/building-firefox-for-windows-on-try-using-taskcluster.html" />
			<rdf:li rdf:resource="tag:blogger.com,1999:blog-2324292491075571582.post-5817496078568719252" />
			<rdf:li rdf:resource="http://jonasfj.dk/?p=435" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5289" />
			<rdf:li rdf:resource="http://www.chesnok.com/daily/?p=5306" />
			<rdf:li rdf:resource="http://yourdomain.com/mozilla,%20ci/2015/02/23/taskcluster-update.html" />
			<rdf:li rdf:resource="http://yourdomain.com/mozilla,%20ci,%20gaia/2014/05/27/gaia-taskcluster-treeherder.html" />
			<rdf:li rdf:resource="http://yourdomain.com/mozilla,%20ci/2014/03/04/taskcluster.html" />
			<rdf:li rdf:resource="http://yourdomain.com/2014/01/31/docker-volumes.html" />
		</rdf:Seq>
	</items>
</channel>

<item rdf:about="http://code.v.igoro.us/posts/2020/12/tc-db-part-3.html">
	<title>Dustin Mitchell: Taskcluster's DB (Part 3) - Online Migrations</title>
	<link>http://code.v.igoro.us/posts/2020/12/tc-db-part-3.html</link>
	<content:encoded>&lt;p&gt;This is part 3 of a deep-dive into the implementation details of &lt;a href=&quot;https://taskcluster.net&quot;&gt;Taskcluster&lt;/a&gt;’s backend data stores.
If you missed the first two, see &lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html&quot;&gt;part 1&lt;/a&gt; and &lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html&quot;&gt;part 2&lt;/a&gt; for the background, as we’ll jump right in here!&lt;/p&gt;

&lt;h3&gt;Big Data&lt;/h3&gt;

&lt;p&gt;A few of the tables holding data for Taskcluster contain a tens or hundreds of millions of lines.
That’s not what the cool kids mean when they say “Big Data”, but it’s big enough that migrations take a long time.
Most changes to Postgres tables take a full lock on that table, preventing other operations from occurring while the change takes place.
The duration of the operation depends on lots of factors, not just of the data already in the table, but on the kind of other operations going on at the same time.&lt;/p&gt;

&lt;p&gt;The usual approach is to schedule a system downtime to perform time-consuming database migrations, and that’s just what we did in July.
By running it a clone of the production database, we determined that we could perform the migration completely in six hours.
It turned out to take a lot longer than that.
Partly, this was because we missed some things when we shut the system down, and left some concurrent operations running on the database.
But by the time we realized that things were moving too slowly, we were near the end of our migration window and had to roll back.
The time-consuming migration was &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/main/db/versions/0020-migration.sql&quot;&gt;version 20 - migrate queue_tasks&lt;/a&gt;, and it had been estimated to take about 4.5 hours.&lt;/p&gt;

&lt;p&gt;When we rolled back, the DB was at version 19, but the code running the Taskcluster services corresponded to version 12.
Happily, we had planned for this situation, and the redefined stored functions described in &lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html&quot;&gt;part 2&lt;/a&gt; bridged the gap with no issues.&lt;/p&gt;

&lt;h3&gt;Patch-Fix&lt;/h3&gt;

&lt;p&gt;Our options were limited: scheduling another extended outage would have been difficult.
We didn’t solve all of the mysteries of the poor performance, either, so we weren’t confident in our prediction of the time required.&lt;/p&gt;

&lt;p&gt;The path we chose was to perform an “online migration”.
I wrote a &lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html&quot;&gt;custom migration script&lt;/a&gt; to accomplish this.
Let’s look at how that worked.&lt;/p&gt;

&lt;p&gt;The goal of the migration was to rewrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;queue_task_entities&lt;/code&gt; table into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt; table, with a few hundred million rows.
The idea with the online migration was to create an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt; table (a very quick operation), then rewrite the stored functions to write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt;, while reading from both tables.
Then a background task can move rows from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;queue_task_entitites&lt;/code&gt; table to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt; table without blocking concurrent operations.
Once the old table is empty, it can be removed and the stored functions rewritten to address only the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;A few things made this easier than it might have been.
Taskcluster’s tasks have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deadline&lt;/code&gt; after which they become immutable, typically within one week of the task’s creation.
That means that the task mutation functions can change the task in-place in whichever table they find it in.
The background task only moves tasks with deadlines in the past.
This eliminates any concerns about data corruption if a row is migrated while it is being modified.&lt;/p&gt;

&lt;p&gt;A look at the script linked above shows that there were some complicating factors, too – notably, two more tables to manage – but those factors didn’t change the structure of the migration.&lt;/p&gt;

&lt;p&gt;With this in place, we ran the replacement migration script, creating the new tables and updating the stored functions.
Then a &lt;a href=&quot;https://github.com/djmitche/taskcluster/blob/393053b0ec3eaebacd75d5b3ed8bc9510d40f8af/db/src/twenty.js&quot;&gt;one-off JS script&lt;/a&gt; drove migration of post-deadline tasks with a rough ETA calculation.
We figured this script would run for about a week, but in fact it was done in just a few days.
Finally, we cleaned up the temporary functions, leaving the DB in precisely the state that the original migration script would have generated.&lt;/p&gt;

&lt;h3&gt;Supported Online Migrations&lt;/h3&gt;

&lt;p&gt;After this experience, we knew we would run into future situations where a “regular” migration would be too slow.
Apart from that, we want users to be able to deploy Taskcluster without scheduling downtimes: requiring downtimes will encourage users to stay at old versions, missing features and bugfixes and increasing our maintenance burden.&lt;/p&gt;

&lt;p&gt;We devised a system to support &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/main/libraries/postgres#online-migrations-1&quot;&gt;online migrations&lt;/a&gt; in any migration.
Its structure is pretty simple: after each migration script is complete, the harness that handles migrations calls a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_batch&lt;/code&gt; stored function repeatedly until it signals that it is complete.
This process can be interrupted and restarted as necessary.
The “cleanup” portion (dropping unnecessary tables or columns and updating stored functions) must be performed in a subsequent DB version.&lt;/p&gt;

&lt;p&gt;The harness is careful to call the &lt;em&gt;previous&lt;/em&gt; version’s online-migration function before it starts a version’s upgrade, to ensure it is complete.
As with the old “quick” migrations, all of this is also supported in reverse to perform a downgrade.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_batch&lt;/code&gt; functions are passed a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; parameter that they can use as a bookmark.
For example, a migration of the tasks might store the last taskId that it migrated in its state.
Then each batch can begin with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select .. where task_id &amp;gt; last_task_id&lt;/code&gt;, allowing Postgres to use the index to quickly find the next task to be migrated.
When the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_batch&lt;/code&gt; function indicates that it processed zero rows, the handler calls an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_is_completed&lt;/code&gt; function.
If this function returns false, then the whole process starts over with an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt;.
This is useful for tables where more rows that were skipped during the migration, such as tasks with deadlines in the future.&lt;/p&gt;

&lt;h4&gt;Testing&lt;/h4&gt;

&lt;p&gt;An experienced engineer is, at this point, boggling at the number of ways this could go wrong!
There are lots of points at which a migration might fail or be interrupted, and the operators might then begin a downgrade.
Perhaps that downgrade is then interrupted, and the migration re-started!
A stressful moment like this is the last time anyone wants surprises, but these are precisely the circumstances that are easily forgotten in testing.&lt;/p&gt;

&lt;p&gt;To address this, and to make such testing easier, we developed a &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/00dd1d65b8ad5e89668dd87478103f10fd4c3861/db/test/helper.js#L235-L270&quot;&gt;test framework&lt;/a&gt; that defines a suite of tests for all manner of circumstances.
In each case, it uses callbacks to verify proper functionality at every step of the way.
It tests both the “happy path” of a successful migration and the “unhappy paths” involving failed migrations and downgrades.&lt;/p&gt;

&lt;h3&gt;In Practice&lt;/h3&gt;

&lt;p&gt;The impetus to actually implement support for online migrations came from some work that &lt;a href=&quot;https://github.com/alopezz&quot;&gt;Alex Lopez&lt;/a&gt; has been doing to change the representation of worker pools in the queue.
This requires rewriting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks&lt;/code&gt; table to transform the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provisioner_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_type&lt;/code&gt; columns into a single, slash-separated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task_queue_id&lt;/code&gt; column.
The &lt;a href=&quot;https://github.com/taskcluster/taskcluster/pull/3987/files&quot;&gt;pull request&lt;/a&gt; is still in progress as I write this, but already serves as a great practical example of an online migration (and online dowgrade, and tests).&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;As we’ve seen in this three-part series, Taskcluster’s data backend has undergone a radical transformation this year, from a relatively simple NoSQL service to a full Postgres database with sophisticated support for ongoing changes to the structure of that DB.&lt;/p&gt;

&lt;p&gt;In some respects, Taskcluster is no different from countless other web services abstracting over a data-storage backend.
Indeed, Django provides robust support for database migrations, as do many other application frameworks.
One factor that sets Taskcluster apart is that it is a “shipped” product, with semantically-versioned releases which users can deploy on their own schedule.
Unlike for a typical web application, we – the software engineers – are not “around” for the deployment process, aside from the Mozilla deployments.
So, we must make sure that the migrations are well-tested and will work properly in a variety of circumstances.&lt;/p&gt;

&lt;p&gt;We did all of this with minimal downtime and no data loss or corruption.
This involved thousands of lines of new code written, tested, and reviewed; a new language (SQL) for most of us; and lots of close work with the Cloud Operations team to perform dry runs, evaluate performance, and debug issues.
It couldn’t have happened without the hard work and close collaboration of the whole Taskcluster team.
Thanks to the team, and thanks to you for reading this short series!&lt;/p&gt;</content:encoded>
	<dc:date>2020-12-02T14:45:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html">
	<title>Dustin Mitchell: Taskcluster's DB (Part 2) - DB Migrations</title>
	<link>http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html</link>
	<content:encoded>&lt;p&gt;This is part 2 of a deep-dive into the implementation details of &lt;a href=&quot;https://taskcluster.net&quot;&gt;Taskcluster&lt;/a&gt;’s backend data stores.
Check out &lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html&quot;&gt;part 1&lt;/a&gt; for the background, as we’ll jump right in here!&lt;/p&gt;

&lt;h3&gt;Azure in Postgres&lt;/h3&gt;

&lt;p&gt;As of the end of April, we had all of our data in a Postgres database, but the data was pretty ugly.
For example, here’s a record of a worker as recorded by worker-manager:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;partition_key | testing!2Fstatic-workers
row_key       | cc!2Fdd~ee!2Fff
value         | {
    &quot;state&quot;: &quot;requested&quot;,
    &quot;RowKey&quot;: &quot;cc!2Fdd~ee!2Fff&quot;,
    &quot;created&quot;: &quot;2015-12-17T03:24:00.000Z&quot;,
    &quot;expires&quot;: &quot;3020-12-17T03:24:00.000Z&quot;,
    &quot;capacity&quot;: 2,
    &quot;workerId&quot;: &quot;ee/ff&quot;,
    &quot;providerId&quot;: &quot;updated&quot;,
    &quot;lastChecked&quot;: &quot;2017-12-17T03:24:00.000Z&quot;,
    &quot;workerGroup&quot;: &quot;cc/dd&quot;,
    &quot;PartitionKey&quot;: &quot;testing!2Fstatic-workers&quot;,
    &quot;lastModified&quot;: &quot;2016-12-17T03:24:00.000Z&quot;,
    &quot;workerPoolId&quot;: &quot;testing/static-workers&quot;,
    &quot;__buf0_providerData&quot;: &quot;eyJzdGF0aWMiOiJ0ZXN0ZGF0YSJ9Cg==&quot;,
    &quot;__bufchunks_providerData&quot;: 1
  }
version       | 1
etag          | 0f6e355c-0e7c-4fe5-85e3-e145ac4a4c6c
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To reap the goodness of a relational database, that would be a “normal”[*] table: distinct columns, nice data types, and a lot less redundancy.&lt;/p&gt;

&lt;p&gt;All access to this data is via some Azure-shaped stored functions, which are also not amenable to the kinds of flexible data access we need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_load&lt;/code&gt; - load a single row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_create&lt;/code&gt; - create a new row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_remove&lt;/code&gt; - remove a row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_modify&lt;/code&gt; - modify a row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_scan&lt;/code&gt; - return some or all rows in the table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;[*] In the normal sense of the word – we did not attempt to apply &lt;a href=&quot;https://en.wikipedia.org/wiki/Database_normalization&quot;&gt;database normalization&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;Database Migrations&lt;/h3&gt;

&lt;p&gt;So the next step, which we dubbed “phase 2”, was to migrate this schema to one more appropriate to the structure of the data.&lt;/p&gt;

&lt;p&gt;The typical approach is to use database migrations for this kind of work, and there are lots of tools for the purpose.
For example, Alembic and Django both provide robust support for database migrations – but they are both in Python.&lt;/p&gt;

&lt;p&gt;The only mature JS tool is &lt;a href=&quot;https://classic.yarnpkg.com/en/package/knex&quot;&gt;knex&lt;/a&gt;, and after some analysis we determined that it both lacked features we needed and brought a lot of additional features that would complicate our usage.
It is primarily a “query builder”, with basic support for migrations.
Because we target Postgres directly, and because of how we use stored functions, a query builder is not useful.
And the migration support in knex, while effective, does not support the more sophisticated approaches to avoiding downtime outlined below.&lt;/p&gt;

&lt;p&gt;We elected to roll our own tool, allowing us to get exactly the behavior we wanted.&lt;/p&gt;

&lt;h4&gt;Migration Scripts&lt;/h4&gt;

&lt;p&gt;Taskcluster defines a sequence of &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/main/db/versions#database-versions&quot;&gt;numbered database versions&lt;/a&gt;.
Each version corresponds to a specific database schema, which includes the structure of the database tables as well as stored functions.
The YAML file for each version specifies a script to upgrade from the previous version, and a script to downgrade back to that version.
For example, an upgrade script might add a new column to a table, with the corresponding downgrade dropping that column.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;version: 29
migrationScript: |-
  begin
    alter table secrets add column last_used timestamptz;
  end
downgradeScript: |-
  begin
    alter table secrets drop column last_used;
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So far, this is a pretty normal approach to migrations.
However, a major drawback is that it requires careful coordination around the timing of the migration and deployment of the corresponding code.
Continuing the example of adding a new column, if the migration is deployed first, then the existing code may execute INSERT queries that omit the new column.
If the new code is deployed first, then it will attempt to read a column that does not yet exist.&lt;/p&gt;

&lt;p&gt;There are workarounds for these issues.
In this example, adding a default value for the new column in the migration, or writing the queries such that they are robust to a missing column.
Such queries are typically spread around the codebase, though, and it can be difficult to ensure (by testing, of course) that they all operate correctly.&lt;/p&gt;

&lt;p&gt;In practice, most uses of database migrations are continuously-deployed applications – a single website or application server, where the developers of the application control the timing of deployments.
That allows a great deal of control, and changes can be spread out over several migrations that occur in rapid succession.&lt;/p&gt;

&lt;p&gt;Taskcluster is not continuously deployed – it is released in distinct versions which users can deploy on their own cadence.
So we need a way to run migrations when upgrading to a new Taskcluster release, without breaking running services.&lt;/p&gt;

&lt;h4&gt;Stored Functions&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html&quot;&gt;Part 1&lt;/a&gt; mentioned that all access to data is via stored functions.
This is the critical point of abstraction that allows smooth migrations, because stored functions can be changed at runtime.&lt;/p&gt;

&lt;p&gt;Each database version specifies definitions for stored functions, either introducing new functions or replacing the implementation of existing functions.
So the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version: 29&lt;/code&gt; YAML above might continue with&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;methods:
  create_secret:
    args: name text, value jsonb
    returns: ''
    body: |-
      begin
        insert
        into secrets (name, value, last_used)
        values (name, value, now());
      end
  get_secret:
    args: name text
    returns: record
    body: |-
      begin
        update secrets
        set last_used = now()
        where secrets.name = get_secret.name;

        return query
        select name, value from secrets
        where secrets.name = get_secret.name;
      end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This redefines two existing functions to operate properly against the new table.
The functions are redefined in the same database transaction as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrationScript&lt;/code&gt; above, meaning that any calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_secret&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_secret&lt;/code&gt; will immediately begin populating the new column.
A critical rule (enforced in code) is that the arguments and return type of a function cannot be changed.&lt;/p&gt;

&lt;p&gt;To support new code that references the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last_used&lt;/code&gt; value, we add a new function:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  get_secret_with_last_used:
    args: name text
    returns: record
    body: |-
      begin
        update secrets
        set last_used = now()
        where secrets.name = get_secret.name;

        return query
        select name, value, last_used from secrets
        where secrets.name = get_secret.name;
      end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another critical rule is that DB migrations must be applied fully before the corresponding version of the JS code is deployed.
In this case, that means code that uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_secret_with_last_used&lt;/code&gt; is deployed only after the function is defined.&lt;/p&gt;

&lt;p&gt;All of this can be thoroughly tested in isolation from the rest of the Taskcluster code, both &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/main/db/test/fns&quot;&gt;unit tests for the functions&lt;/a&gt; and &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/main/db/test/versions&quot;&gt;integration tests for the upgrade and downgrade scripts&lt;/a&gt;.
Unit tests for redefined functions should continue to pass, unchanged, providing an easy-to-verify compatibility check.&lt;/p&gt;

&lt;h3&gt;Phase 2 Migrations&lt;/h3&gt;

&lt;p&gt;The migrations from Azure-style tables to normal tables are, as you might guess, a lot more complex than this simple example.
Among the issues we faced:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Azure-entities uses a &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0008-migration.sql#L25-L63&quot;&gt;multi-field base64 encoding&lt;/a&gt; for many data-types, that must be decoded (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__buf0_providerData&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__bufchunks_providerData&lt;/code&gt; in the example above)&lt;/li&gt;
  &lt;li&gt;Partition and row keys are &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0008-migration.sql#L65-L123&quot;&gt;encoded using a custom variant of urlencoding&lt;/a&gt; that is remarkably difficult to implement in pl/pgsql&lt;/li&gt;
  &lt;li&gt;Some columns (such as secret values) are &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0034-migration.sql#L3-L49&quot;&gt;encrypted&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Postgres generates &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0025-migration.sql#L2-L12&quot;&gt;slightly different ISO8601 timestamps&lt;/a&gt; from JS’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date.toJSON()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We split the work of performing these migrations across the members of the Taskcluster team, supporting each other through the tricky bits, in a rather long but ultimately successful &lt;a href=&quot;https://github.com/taskcluster/taskcluster/milestone/1?closed=1&quot;&gt;“Postgres Phase 2” sprint&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;0042 - secrets phase 2&lt;/h4&gt;

&lt;p&gt;Let’s look at one of the simpler examples: the secrets service.
The &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/main/db/versions/0042-migration.sql&quot;&gt;migration script&lt;/a&gt; creates a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets&lt;/code&gt; table from the data in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets_entities&lt;/code&gt; table, using Postgres’s JSON function to unpack the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; column into “normal” columns.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/main/db/versions/0042.yml&quot;&gt;database version YAML file&lt;/a&gt; carefully redefines the Azure-compatible DB functions to access the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets&lt;/code&gt; table.
This involves unpacking function arguments from their JSON formats, re-packing JSON blobs for return values, and even some light &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0042.yml#L92-L101&quot;&gt;parsing of the condition string&lt;/a&gt; for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets_entities_scan&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;It then defines &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/b0c607f587590490709b007db49519ca0aa088f9/db/versions/0042.yml#L130-L205&quot;&gt;new stored functions for direct access to the normal table&lt;/a&gt;.
These functions are typically similar, and more specific to the needs of the service.
For example, the secrets service only modifies secrets in an “upsert” operation that replaces any existing secret of the same name.&lt;/p&gt;

&lt;h5&gt;Step By Step&lt;/h5&gt;

&lt;p&gt;To achieve an extra layer of confidence in our work, we landed all of the phase-2 PRs in two steps.
The &lt;a href=&quot;https://github.com/taskcluster/taskcluster/pull/3280/commits/2399e5f838145692c9d7d4af76b549a1346b7160&quot;&gt;first step&lt;/a&gt; included migration and downgrade scripts and the redefined stored functions, as well as tests for those.
But critically, this step did not modify the service using the table (the secrets service in this case).
So the unit tests for that service use the redefined stored functions, acting as a kind of integration-test for their implementations.
This also validates that the service will continue to run in production between the time the database migration is run and the time the new code is deployed.
We landed this step on GitHub in such a way that reviewers could see a green check-mark on the step-1 commit.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;https://github.com/taskcluster/taskcluster/pull/3280/commits/fd1a0f70115d2966661e28249a8ed86f21a54883&quot;&gt;second step&lt;/a&gt;, we added the new, purpose-specific stored functions and rewrote the service to use them.
In services like secrets, this was a simple change, but some other services saw more substantial rewrites due to more complex requirements.&lt;/p&gt;

&lt;h3&gt;Deprecation&lt;/h3&gt;

&lt;p&gt;Naturally, we can’t continue to support old functions indefinitely: eventually they would be prohibitively complex or simply impossible to implement.
Another deployment rule provides a critical escape from this trap: Taskcluster must be upgraded at most one major version at a time (e.g., 36.x to 37.x).
That provides a limited window of development time during which we must maintain compatibility.&lt;/p&gt;

&lt;p&gt;Defining that window is surprisingly tricky, but essentially it’s two major revisions.
Like the software engineers we are, we packaged up that tricky computation in a script, and include the lifetimes in some &lt;a href=&quot;https://github.com/taskcluster/taskcluster/blob/aa991b3af2abfe7730d78c73e1c80d3d5d9644ba/db/fns.md#deprecated-methods&quot;&gt;generated documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;What’s Next?&lt;/h3&gt;

&lt;p&gt;This post has hinted at some of the complexity of “phase 2”.
There are lots of details omitted, of course!&lt;/p&gt;

&lt;p&gt;But there’s one major detail that got us in a bit of trouble.
In fact, we were forced to roll back during a planned migration – not an engineer’s happiest moment.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;queue_tasks_entities&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;queue_artifacts_entities&lt;/code&gt; table were just too large to migrate in any reasonable amount of time.
Part 3 will describe what happened, how we fixed the issue, and what we’re doing to avoid having the same issue again.&lt;/p&gt;</content:encoded>
	<dc:date>2020-10-30T19:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html">
	<title>Dustin Mitchell: Taskcluster's DB (Part 1) - Azure to Postgres</title>
	<link>http://code.v.igoro.us/posts/2020/10/tc-db-part-1.html</link>
	<content:encoded>&lt;p&gt;This is a deep-dive into some of the implementation details of &lt;a href=&quot;https://taskcluster.net&quot;&gt;Taskcluster&lt;/a&gt;.
Taskcluster is a platform for building continuous integration, continuous deployment, and software-release processes.
It’s an &lt;a href=&quot;https://github.com/taskcluster/taskcluster&quot;&gt;open source project&lt;/a&gt; that began life at Mozilla, supporting the Firefox build, test, and release systems.&lt;/p&gt;

&lt;p&gt;The Taskcluster “services” are a collection of microservices that handle distinct tasks: the queue coordinates tasks; the worker-manager creates and manages workers to execute tasks; the auth service authenticates API requests; and so on.&lt;/p&gt;

&lt;h3&gt;Azure Storage Tables to Postgres&lt;/h3&gt;

&lt;p&gt;Until April 2020, Taskcluster stored its data in Azure Storage tables, a simple NoSQL-style service similar to AWS’s DynamoDB.
Briefly, each Azure table is a list of JSON objects with a single primary key composed of a partition key and a row key.
Lookups by primary key are fast and parallelize well, but scans of an entire table are extremely slow and subject to API rate limits.
Taskcluster was carefully designed within these constraints, but that meant that some useful operations, such as listing tasks by their task queue ID, were simply not supported.
Switching to a fully-relational datastore would enable such operations, while easing deployment of the system for organizations that do not use Azure.&lt;/p&gt;

&lt;h4&gt;Always Be Migratin’&lt;/h4&gt;

&lt;p&gt;In April, we migrated the existing deployments of Taskcluster (at that time all within Mozilla) to Postgres.
This was a “forklift migration”, in the sense that we moved the data directly into Postgres with minimal modification.
Each Azure Storage table was imported into a single Postgres table of the same name, with a fixed structure:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;create table queue_tasks_entities(
    partition_key text,
    row_key text,
    value jsonb not null,
    version integer not null,
    etag uuid default public.gen_random_uuid()
);
alter table queue_tasks_entities add primary key (partition_key, row_key);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/helfi92/taskcluster/blob/6b3220305124388803da060811d8ea584f92aefe/infrastructure/tooling/src/importer/importer.js&quot;&gt;importer&lt;/a&gt; we used was specially tuned to accomplish this import in a reasonable amount of time (hours).
For each known deployment, we scheduled a downtime to perform this migration, after extensive performance testing on development copies.&lt;/p&gt;

&lt;p&gt;We considered options to support a downtime-free migration.
For example, we could have built an adapter that would read from Postgres and Azure, but write to Postgres.
This adapter could support production use of the service while a background process copied data from Azure to Postgres.&lt;/p&gt;

&lt;p&gt;This option would have been very complex, especially in supporting some of the atomicity and ordering guarantees that the Taskcluster API relies on.
Failures would likely lead to data corruption and a downtime much longer than the simpler, planned downtime.
So, we opted for the simpler, planned migration.
(we’ll revisit the idea of online migrations in part 3)&lt;/p&gt;

&lt;p&gt;The database for &lt;a href=&quot;https://firefox-ci-tc.services.mozilla.com/&quot;&gt;Firefox CI&lt;/a&gt; occupied about 350GB.
The other deployments, such as the &lt;a href=&quot;https://community-tc.services.mozilla.com/&quot;&gt;community deployment&lt;/a&gt;, were much smaller.&lt;/p&gt;

&lt;h4&gt;Database Interface&lt;/h4&gt;

&lt;p&gt;All access to Azure Storage tables had been via the &lt;a href=&quot;https://github.com/taskcluster/azure-entities&quot;&gt;azure-entities&lt;/a&gt; library, with a limited and very regular interface (hence the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_entities&lt;/code&gt; suffix on the Postgres table name).
We wrote an implementation of the same interface, but with a Postgres backend, in &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/23e2fb746068f6e647946347eebffddd5e1b93fb/libraries/entities&quot;&gt;taskcluster-lib-entities&lt;/a&gt;.
The result was that none of the code in the Taskcluster microservices changed.
Not changing code is a great way to avoid introducing new bugs!
It also limited the complexity of this change: we only had to deeply understand the semantics of azure-entities, and not the details of how the queue service handles tasks.&lt;/p&gt;

&lt;h4&gt;Stored Functions&lt;/h4&gt;

&lt;p&gt;As the &lt;a href=&quot;https://github.com/taskcluster/taskcluster/tree/23e2fb746068f6e647946347eebffddd5e1b93fb/libraries/entities&quot;&gt;taskcluster-lib-entities&lt;/a&gt; README indicates, access to each table is via five stored database functions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_load&lt;/code&gt; - load a single row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_create&lt;/code&gt; - create a new row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_remove&lt;/code&gt; - remove a row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_modify&lt;/code&gt; - modify a row&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tableName&amp;gt;_scan&lt;/code&gt; - return some or all rows in the table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stored functions are functions defined in the database itself, that can be redefined within a transaction.
&lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html&quot;&gt;Part 2&lt;/a&gt; will get into why we made this choice.&lt;/p&gt;

&lt;h5&gt;Optimistic Concurrency&lt;/h5&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modify&lt;/code&gt; function is an interesting case.
Azure Storage has no notion of a “transaction”, so the azure-entities library uses an optimistic-concurrency approach to implement atomic updates to rows.
This uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etag&lt;/code&gt; column, which changes to a new value on every update, to detect and retry concurrent modifications.
While Postgres can do much better, we replicated this behavior in taskcluster-lib-entities, again to limit the changes made and avoid introducing new bugs.&lt;/p&gt;

&lt;p&gt;A modification looks like this in Javascript:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;await task.modify(task =&amp;gt; {
  if (task.status !== 'running') {
    task.status = 'running';
    task.started = now();
  }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For those not familiar with JS notation, this is calling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modify&lt;/code&gt; method on a task, passing a modifier function which, given a task, modifies that task.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modify&lt;/code&gt; method calls the modifier and tries to write the updated row to the database, conditioned on the etag still having the value it did when the task was loaded.
If the etag does not match, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modify&lt;/code&gt; re-loads the row to get the new etag, and tries again until it succeeds.
The effect is that updates to the row occur one-at-a-time.&lt;/p&gt;

&lt;p&gt;This approach is “optimistic” in the sense that it assumes no conflicts, and does extra work (retrying the modification) only in the unusual case that a conflict occurs.&lt;/p&gt;

&lt;h4&gt;What’s Next?&lt;/h4&gt;

&lt;p&gt;At this point, we had fork-lifted Azure tables into Postgres and no longer require an Azure account to run Taskcluster.
However, we hadn’t yet seen any of the benefits of a relational database:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;data fields were still trapped in a JSON object (in fact, some kinds of data were hidden in base64-encoded blobs)&lt;/li&gt;
  &lt;li&gt;each table still only had a single primary key, and queries by any other field would still be prohibitively slow&lt;/li&gt;
  &lt;li&gt;joins between tables would also be prohibitively slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://code.v.igoro.us/posts/2020/10/tc-db-part-2.html&quot;&gt;Part 2&lt;/a&gt; of this series of articles will describe how we addressed these issues.
Then part 3 will get into the details of performing large-scale database migrations without downtime.&lt;/p&gt;</content:encoded>
	<dc:date>2020-10-28T21:00:00+00:00</dc:date>
</item>
<item rdf:about="http://petemoore.github.io/spectrum/2020/08/24/zx-spectrum-plus-4.html">
	<title>Pete Moore: ZX Spectrum +4 - kind of</title>
	<link>http://petemoore.github.io/spectrum/2020/08/24/zx-spectrum-plus-4.html</link>
	<content:encoded>&lt;p&gt;The ZX Spectrum +2A was my first computer, and I really loved it. On it, I
learned to program (badly), and learned how computers worked (or so I thought).
I started writing my &lt;a href=&quot;https://github.com/petemoore/zxspectrum&quot;&gt;first computer
programs&lt;/a&gt; in &lt;a href=&quot;https://en.wikipedia.org/wiki/Sinclair_BASIC&quot;&gt;Sinclair
BASIC&lt;/a&gt;, and tinkered a little
with Z80 machine code (I didn’t have an assembler, but I did have a book that
documented the opcodes and operand encodings for each of the Z80 assembly
mnemonics).&lt;/p&gt;

&lt;p&gt;Fast forward 25 years, and I found myself middle aged, having spent my career
thus far as a programmer, but never writing any assembly (let alone machine
code), and having lost touch with the low level computing techniques that
attracted me to programming in the first place.&lt;/p&gt;

&lt;p&gt;So I decided to change that, and start a new project. My idea was to adapt the
original Spectrum 128K ROM from Z80 machine code, to 64 bit ARM assembly,
running on the Raspberry Pi 3B (which I happened to own). The idea was not to
create an emulator (there are plenty of those around), but instead, to create a
new “operating system” (probably &lt;em&gt;monitor program&lt;/em&gt; would be accurate term) that
had roughly the same feature set as the original Spectrum computers, but
designed to run on modern hardware, at faster speeds, with higher resolution,
more sophisticated sound etc.&lt;/p&gt;

&lt;p&gt;What I loved about the original Spectrum, was the ease at which you could learn
to program, and the simplicity of the platform. You did not need to study
computer science for 30 years to understand it. That isn’t true with modern
operating systems, they are much more complex. I wanted to create something
simple and intuitive again, that would provide a similar computing experience.
Said another way, I wanted to create something with the sophistication of the
original spectrum, but that would run on modern hardware. Since it was meant to
be an evolution of the Spectrum, I decided to call it the ZX Spectrum +4 (since
the ZX Spectrum +3 was the last Spectrum that was ever made).&lt;/p&gt;

&lt;p&gt;Well it is a work-in-progress, and has been a lot of fun to write so far.
Please feel free to get involved with the project, and leave a comment, or open
an issue or pull request against the repository. I think I have a fair bit of
work to do, but it is doable. The original Spectrum ROMs were 16K each, so
there is 32Kb of Z80 machine code and tables to translate, but given that
instructions are variable length (typically 1, 2, or 3 bytes) there are
probably something like 15,000 instructions to translate, which could be a year
or two of hobby time, given my other commitments. Or less, if other people get
involved! :-)&lt;/p&gt;

&lt;p&gt;The github repo can be found &lt;a href=&quot;https://github.com/spectrum4/spectrum4&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2020-08-24T14:49:27+00:00</dc:date>
</item>
<item rdf:about="http://petemoore.github.io/general/2020/08/24/nt-rights-open-source-alternative.html">
	<title>Pete Moore: Open Source Alternative to ntrights.exe</title>
	<link>http://petemoore.github.io/general/2020/08/24/nt-rights-open-source-alternative.html</link>
	<content:encoded>&lt;p&gt;If you wish to modify LSA policy programmatically on Windows, the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntrights.exe&lt;/code&gt; utility from the Windows 2000 Server Resource Kit may help you.
But if you need to ship a product that uses it, you may wish to consider an
open source tool to avoid any licensing issues.&lt;/p&gt;

&lt;p&gt;Needing to do a similar thing myself, I’ve written the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntr&lt;/code&gt; open source
utility for this purpose. It contains both a standalone executable (like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntrights.exe&lt;/code&gt;) and a Go library interface.&lt;/p&gt;

&lt;p&gt;The project is on github &lt;a href=&quot;https://github.com/petemoore/ntr&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you find it useful!&lt;/p&gt;</content:encoded>
	<dc:date>2020-08-24T14:32:13+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2020/05/debugging-docker-connection-reset-by-peer.html">
	<title>Dustin Mitchell: Debugging Docker Connection Reset by Peer</title>
	<link>http://code.v.igoro.us/posts/2020/05/debugging-docker-connection-reset-by-peer.html</link>
	<content:encoded>&lt;p&gt;(this post is co-written with &lt;a href=&quot;https://github.com/imbstack&quot;&gt;@imbstack&lt;/a&gt; and cross-posted on &lt;a href=&quot;https://imbstack.com/2020/05/03/debugging-docker-connection-resets.html&quot;&gt;his blog&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;Symptoms&lt;/h3&gt;

&lt;p&gt;At the end of January this year the Taskcluster team was alerted to &lt;a href=&quot;https://github.com/web-platform-tests/wpt/issues/21529&quot;&gt;networking issues in a user’s tasks&lt;/a&gt;. The first
report involved &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETIMEDOUT&lt;/code&gt; but later on it became clear that the more frequent issue was involving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ECONNRESET&lt;/code&gt; in the middle of downloading artifacts necessary to
run the tests in the tasks. It seemed it was only occurring on downloads from Google (https://dl.google.com) on our workers running in GCP, and only with relatively large artifacts. This led
us to initially blame some bit of infrastructure outside of Taskcluster but eventually we found the issue to be with how Docker was handling networking on our worker machines.&lt;/p&gt;

&lt;h3&gt;Investigation&lt;/h3&gt;

&lt;p&gt;The initial stages of the investigation were focused on exploring possible causes of the error and on finding a way to reproduce the error.&lt;/p&gt;

&lt;p&gt;Investigation of an intermittent error in a high-volume system like this is slow and difficult work.
It’s difficult to know if an intervention fixed the issue just because the error does not recur.
And it’s difficult to know if an intervention did &lt;em&gt;not&lt;/em&gt; fix the issue, as “Connection reset by peer” can be due to transient network hiccups.
It’s also difficult to gather data from production systems as the quantity of data per failure is unmanageably high.&lt;/p&gt;

&lt;p&gt;We explored a few possible causes of the issue, all of which turned out to be dead ends.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Rate Limiting or Abuse Prevention&lt;/em&gt; - The TC team has seen cases where downloads from compute clouds were limited as a form of abuse prevention.
Like many CI processes, the WPT jobs download Chrome on every run, and it’s possible that a series of back-to-back tasks on the same worker could appear malicious to an abuse-prevention device.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Outages of the download server&lt;/em&gt; - This was unlikely, given Google’s operational standards, but worth exploring since the issues seemed limited to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dl.google.com&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Exhaustion of Cloud NAT addresses&lt;/em&gt; - Resource exhaustion in the compute cloud might have been related.  This was easily ruled out with the observation that workers are not using Cloud NAT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, several of us were working on reproducing the issue in more controlled circumstances.
This began with interactive sessions on Taskcluster workers, and soon progressed to a script that reproduced the issue easily on a GCP instance running the VM image used to run workers.
An important observation here was that the issue only reproduced &lt;em&gt;inside&lt;/em&gt; of a docker container: downloads from the host worked just fine.
This seemed to affect all docker images, not just the image used in WPT jobs.&lt;/p&gt;

&lt;p&gt;At this point, we were able to use Taskcluster itself to reproduce the issue at scale, creating a &lt;a href=&quot;https://community-tc.services.mozilla.com/tasks/groups/UyRo436cTXC-Zez_lMgzPQ&quot;&gt;task group&lt;/a&gt; of identical tasks running the reproduction recipe.
The “completed” tasks in that group are the successful reproductions.&lt;/p&gt;

&lt;p&gt;Armed with quick, reliable reproduction, we were able to start capturing dumps of the network traffic.
From these, we learned that the downloads were failing mid-download (tens of MB into a ~65MB file).
We were also able to confirm that the error is, indeed, a TCP RST segment from the peer.&lt;/p&gt;

&lt;p&gt;Searches for similar issues around this time found a blog post entitled &lt;a href=&quot;https://medium.com/swlh/fix-a-random-network-connection-reset-issue-in-docker-kubernetes-5c57a11de170&quot;&gt;“Fix a random network Connection Reset issue in Docker/Kubernetes”&lt;/a&gt;, which matched our issue in many respects.
It’s a long read, but the summary is that conntrack, which is responsible for maintaining NAT tables in the Linux kernel, sometimes gets mixed up and labels a valid packet as INVALID.
The default configuration of iptables forwarding rules is to ignore INVALID packets, meaning that they fall through to the default ACCEPT for the FILTER table.
Since the port is not open on the host, the host replies with an RST segment.
Docker containers use NAT to translate between the IP of the container and the IP of the host, so this would explain why the issue only occurs in a Docker container.&lt;/p&gt;

&lt;p&gt;We were, indeed, seeing INVALID packets as revealed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conntrack -S&lt;/code&gt;, but there were some differences from our situation, so we continued investigating.
In particular, in the blog post, the connection errors are seen there in the opposite direction, and involved a local server for which the author had added some explicit firewall rules.&lt;/p&gt;

&lt;p&gt;Since we hypothesized that NAT was involved, we captured packet traces both inside the Docker container and on the host interface, and combined the two.
The results were pretty interesting!
In the dump output below, 74.125.195.136 is dl.google.com, 10.138.0.12 is the host IP, and 172.17.0.2 is the container IP.
10.138.0.12 is a private IP, suggesting that there is an additional layer of NAT going on between the host IP and the Internet, but this was not the issue.&lt;/p&gt;

&lt;p&gt;A “normal” data segment looks like&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;22:26:19.414064 ethertype IPv4 (0x0800), length 26820: 74.125.195.136.https &amp;gt; 10.138.0.12.60790: Flags [.], seq 35556934:35583686, ack 789, win 265, options [nop,nop,TS val 2940395388 ecr 3057320826], length 26752
22:26:19.414076 ethertype IPv4 (0x0800), length 26818: 74.125.195.136.https &amp;gt; 172.17.0.2.60790: Flags [.], seq 35556934:35583686, ack 789, win 265, options [nop,nop,TS val 2940395388 ecr 3057320826], length 26752
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;here the first line is outside the container and the second line is inside the container; the SNAT translation has rewritten the host IP to the container IP.
The sequence numbers give the range of bytes in the segment, as an offset from the initial sequence number, so we are almost 34MB into the download (from a total of about 65MB) at this point.&lt;/p&gt;

&lt;p&gt;We began by looking at the end of the connection, when it failed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;A
22:26:19.414064 ethertype IPv4 (0x0800), length 26820: 74.125.195.136.https &amp;gt; 10.138.0.12.60790: Flags [.], seq 35556934:35583686, ack 789, win 265, options [nop,nop,TS val 2940395388 ecr 3057320826], length 26752
22:26:19.414076 ethertype IPv4 (0x0800), length 26818: 74.125.195.136.https &amp;gt; 172.17.0.2.60790: Flags [.], seq 35556934:35583686, ack 789, win 265, options [nop,nop,TS val 2940395388 ecr 3057320826], length 26752

B
22:26:19.414077 ethertype IPv4 (0x0800), length 2884: 74.125.195.136.https &amp;gt; 10.138.0.12.60790: Flags [.], seq 34355910:34358726, ack 789, win 265, options [nop,nop,TS val 2940395383 ecr 3057320821], length 2816

C
22:26:19.414091 ethertype IPv4 (0x0800), length 56: 10.138.0.12.60790 &amp;gt; 74.125.195.136.https: Flags [R], seq 821696165, win 0, length 0

...
X
22:26:19.416605 ethertype IPv4 (0x0800), length 66: 172.17.0.2.60790 &amp;gt; 74.125.195.136.https: Flags [.], ack 35731526, win 1408, options [nop,nop,TS val 3057320829 ecr 2940395388], length 0
22:26:19.416626 ethertype IPv4 (0x0800), length 68: 10.138.0.12.60790 &amp;gt; 74.125.195.136.https: Flags [.], ack 35731526, win 1408, options [nop,nop,TS val 3057320829 ecr 2940395388], length 0

Y
22:26:19.416715 ethertype IPv4 (0x0800), length 56: 74.125.195.136.https &amp;gt; 10.138.0.12.60790: Flags [R], seq 3900322453, win 0, length 0
22:26:19.416735 ethertype IPv4 (0x0800), length 54: 74.125.195.136.https &amp;gt; 172.17.0.2.60790: Flags [R], seq 3900322453, win 0, length 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Segment (A) is a normal data segment, forwarded to the container.
But (B) has a much lower sequence number, about 1MB earlier in the stream, and it is not forwarded to the docker container.
Notably, (B) is also about 1/10 the size of the normal data segments – we never figured out why that is the case.
Instead, we see an RST segment (C) sent back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dl.google.com&lt;/code&gt;.
This situation repeats a few times: normal segment forwarded, late segment dropped, RST segment sent to peer.&lt;/p&gt;

&lt;p&gt;Finally, the docker container sends an ACK segment (X) for the segments it has received so far, and this is answered by an RST segment (Y) from the peer, and that RST segment is forwarded to the container.
This final RST segment is reasonable from the peer’s perspective: we have already reset its connection, so by the time it gets (X) the connection has been destroyed.
But this is the first the container has heard of any trouble on the connection, so it fails with “Connection reset by peer”.&lt;/p&gt;

&lt;p&gt;So it seems that the low-sequence-number segments are being flagged as INVALID by conntrack and causing it to send RST segments.
That’s a little surprising – why is conntrack paying attention to sequence numbers at all?
From &lt;a href=&quot;https://www.usenix.org/legacy/events/sec01/invitedtalks/rooij.pdf&quot;&gt;this article&lt;/a&gt; it appears this is a security measure, helping to protect sockets behind the NAT from various attacks on TCP.&lt;/p&gt;

&lt;p&gt;The second surprise here is that such late TCP segments are present.
Scrolling back through the dump output, there are many such packets – enough that manually labeling them is infeasible.
However, graphing the sequence numbers shows a clear pattern:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;sequence number graph&quot; src=&quot;http://code.v.igoro.us/img/tcp-seq-graph.png&quot; style=&quot;width: 100%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that this covers only the last 16ms of the connection (the horizontal axis is in seconds), carrying about 200MB of data (the vertical axis is sequence numbers, indicating bytes).
The “fork” in the pattern shows a split between the up-to-date segments, which seem to accelerate, and the delayed segments.
The delayed segments are only slightly delayed - 2-3ms.
But a spot-check of a few sequence ranges in the dump shows that they had already been retransmitted by the time they were delivered.
When such late segments were not dropped by conntrack, the receiver replied to them with what’s known as a duplicate ACK, a form of selective ACK that says “I have received that segment, and in fact I’ve received many segments since then.”&lt;/p&gt;

&lt;p&gt;Our best guess here is that some network intermediary has added a slight delay to some packets.
But since the RTT on this connection is so short, that delay is relatively huge and puts the delayed packets outside of the window where conntrack is willing to accept them.
That helps explain why other downloads, from hosts outside of the Google infrastructure, do not see this issue: either they do not traverse the intermediary delaying these packets, or the RTT is long enough that a few ms is not enough to result in packets being marked INVALID.&lt;/p&gt;

&lt;h3&gt;Resolution&lt;/h3&gt;

&lt;p&gt;After we posted these results in the issue, our users realized these symptoms looked a lot like &lt;a href=&quot;https://github.com/moby/libnetwork/issues/1090&quot;&gt;a Moby libnetwork bug&lt;/a&gt;. We adopted a workaround
mentioned there where we use conntrack to drop invalid packets in iptables rather than trigger RSTs&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iptables -I INPUT -m conntrack --ctstate INVALID -j DROP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The drawbacks of that approach listed in the bug are acceptable for our uses. After baking a new machine images we tried to reproduce the issue at scale as we had done during the debugging
of this issue and were not able to. We updated all of our worker pools to use this image the next day and it seems like we’re now in the clear.&lt;/p&gt;

&lt;h3&gt;Security Implications&lt;/h3&gt;

&lt;p&gt;As we uncovered this behavior, there was some concern among the team that this represented a security issue.
When conntrack marks a packet as INVALID and it is handled on the host, it’s possible that the same port on the host &lt;em&gt;is&lt;/em&gt; in use, and the packet could be treated as part of that connection.
However, TCP identifies connections with a “four-tuple” of source IP and port + destination IP and port.
But the tuples cannot match, or the remote end would have been unable to distinguish the connection “through” the NAT from the connection terminating on the host.
So there is no issue of confusion between connections here.&lt;/p&gt;

&lt;p&gt;However, there is the possibility of a denial of service.
If an attacker can guess the four-tuple for an existing connection and forge an INVALID packet matching it, the resulting RST would destroy the connection.
This is probably only an issue if the attacker is on the same network as the docker host, as otherwise reverse-path filtering would discard such a forged packet.&lt;/p&gt;

&lt;p&gt;At any rate, this issue appears to be fixed in more recent distributions.&lt;/p&gt;

&lt;h3&gt;Thanks&lt;/h3&gt;

&lt;p&gt;@hexcles, @djmitche, @imbstack, @stephenmcgruer&lt;/p&gt;</content:encoded>
	<dc:date>2020-05-06T21:00:00+00:00</dc:date>
</item>
<item rdf:about="https://walac.github.io/booting-packet-images-qemu/">
	<title>Wander Lairson Costa: Running packet.net images in qemu</title>
	<link>https://walac.github.io/booting-packet-images-qemu/</link>
	<content:encoded>&lt;p&gt;For the past months, I have been working on adding Taskcluster support
for &lt;a href=&quot;https://packet.com&quot;&gt;packet.net&lt;/a&gt; cloud provider. The reason for that
is to get faster Firefox for Android CI tests. Tests showed that jobs run
up to 4x faster on bare metal machines than EC2.&lt;/p&gt;

&lt;p&gt;I set up 25 machines to run a small subset of the production tasks, and so
far results are excellent. The problem is that those machines are up 24/7 and there
is no dynamic provisioning. If we need more machines, I have to manually change
the &lt;a href=&quot;https://www.terraform.io&quot;&gt;terraform&lt;/a&gt; script to scale it up. We need a smart
way to do that. We are going to build something similar as
&lt;a href=&quot;https://github.com/taskcluster/aws-provisioner&quot;&gt;aws-provisioner&lt;/a&gt;. However,
we need a custom &lt;strong&gt;packet.net&lt;/strong&gt; image to speed up instance startup.&lt;/p&gt;

&lt;p&gt;The problem is that if you can’t ssh into the machine, there is no way to get
access to it to see what’s wrong. In this post,l I am going to show how you can
run a packet image locally with &lt;a href=&quot;https://www.qemu.org/&quot;&gt;qemu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find documentation about creating custom packet images
&lt;a href=&quot;https://support.packet.com/kb/articles/custom-images&quot;&gt;here&lt;/a&gt; and
&lt;a href=&quot;https://github.com/packethost/packet-images/blob/master/README.md&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s create a sample image for the post. After you clone the
&lt;a href=&quot;https://github.com/packethost/packet-images&quot;&gt;packet-images repo&lt;/a&gt;, run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./tools/build.sh -d ubuntu_14_04 -p t1.small.x86 -a x86_64 -b ubuntu_14_04-t1.small.x86-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This creates the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image.tar.gz&lt;/code&gt; file, which is your packet image.
The goal of this post is not to guide you on creating your custom image; you can refer
to the documentation linked above for this. The goal here is, once you have your
image, how you can run it locally with &lt;a href=&quot;https://www.qemu.org/&quot;&gt;qemu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first step is to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qemu&lt;/code&gt; disk to install the image into it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ qemu-img create -f raw linux.img 10G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qemu&lt;/code&gt; image. We now need to create a disk partition:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cfdisk linux.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dos&lt;/code&gt; for the partition table, create a single primary partition and
make it bootable. We now need to create a loop device to handle our image:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo losetup -Pf linux.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; option looks for the first free loop device for attachment to the image file.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-P&lt;/code&gt; option instructs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;losetup&lt;/code&gt; to read the partition table and create a loop
device for each partition found; this avoids we having to play with disk
offsets. Now let’s find our loop device:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo losetup -l
NAME        SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                                         DIO LOG-SEC
/dev/loop1          0      0         1  1 /var/lib/snapd/snaps/gnome-calculator_260.snap      0     512
/dev/loop8          0      0         1  1 /var/lib/snapd/snaps/gtk-common-themes_818.snap     0     512
/dev/loop6          0      0         1  1 /var/lib/snapd/snaps/core_5662.snap                 0     512
/dev/loop4          0      0         1  1 /var/lib/snapd/snaps/gtk-common-themes_701.snap     0     512
/dev/loop11         0      0         1  1 /var/lib/snapd/snaps/gnome-characters_139.snap      0     512
/dev/loop2          0      0         1  1 /var/lib/snapd/snaps/gnome-calculator_238.snap      0     512
/dev/loop0          0      0         1  1 /var/lib/snapd/snaps/gnome-logs_45.snap             0     512
/dev/loop9          0      0         1  1 /var/lib/snapd/snaps/core_6034.snap                 0     512
/dev/loop7          0      0         1  1 /var/lib/snapd/snaps/gnome-characters_124.snap      0     512
/dev/loop5          0      0         1  1 /var/lib/snapd/snaps/gnome-3-26-1604_70.snap        0     512
/dev/loop12         0      0         0  0 /home/walac/work/packet-images/linux.img            0     512
/dev/loop3          0      0         1  1 /var/lib/snapd/snaps/gnome-system-monitor_57.snap   0     512
/dev/loop10         0      0         1  1 /var/lib/snapd/snaps/gnome-3-26-1604_74.snap        0     512
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We see that our loop device is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/loop12&lt;/code&gt;. If we look in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev&lt;/code&gt; directory:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls -l /dev/loop12*
brw-rw---- 1 root   7, 12 Dec 17 10:39 /dev/loop12
brw-rw---- 1 root 259,  0 Dec 17 10:39 /dev/loop12p1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We see that, thanks to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-P&lt;/code&gt; option, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;losetup&lt;/code&gt; created the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop12p1&lt;/code&gt;
device for the partition we have. It is time to set up the filesystem:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mkfs.ext4 -b 1024 /dev/loop12p1 
mke2fs 1.44.4 (18-Aug-2018)
Discarding device blocks: done                            
Creating filesystem with 10484716 1k blocks and 655360 inodes
Filesystem UUID: 2edfe9f2-7e90-4c35-80e2-bd2e49cad251
Superblock backups stored on blocks: 
        8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409, 663553, 
        1024001, 1990657, 2809857, 5120001, 5971969

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, finally we can mount our device and extract the image to it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mkdir mnt
$ sudo mount /dev/loop12p1 mnt/
$ sudo tar -xzf image.tar.gz -C mnt/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last step is to install the bootloader. As we are running an Ubuntu image,
we will use &lt;a href=&quot;https://www.gnu.org/software/grub/manual/grub/grub.html&quot;&gt;grub2&lt;/a&gt;
for that.&lt;/p&gt;

&lt;p&gt;Firstly we need to install grub in the boot sector:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo grub-install --boot-directory mnt/boot/ /dev/loop12
Installing for i386-pc platform.
Installation finished. No error reported.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we point to the boot directory of our image. Next, we have to
generate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub.cfg&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd mnt/
$ for i in /proc /dev /sys; do sudo mount -B $i .$i; done
$ sudo chroot .
# cd /etc/grub.d/
# chmod -x 30_os-prober
# update-grub
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Found linux image: /boot/vmlinuz-3.13.0-123-generic
Found initrd image: /boot/initrd.img-3.13.0-123-generic
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We &lt;em&gt;bind mount&lt;/em&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys&lt;/code&gt; host mount points inside the Ubuntu image,
then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chroot&lt;/code&gt; into it. Next, to avoid grub creating entries for our host OSes, we disable the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;30_os-prober&lt;/code&gt; script.  Finally we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update-grub&lt;/code&gt; and it creates the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/grub/grub.cfg&lt;/code&gt; file.
Now the only thing left is cleanup:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# exit
$ for i in dev/ sys/ proc/; do sudo umount $i; done
$ cd ..
$ sudo umount mnt/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The commands are self explanatory. Now let’s run our image:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo qemu-system-x86_64 -enable-kvm -hda /dev/loop12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;qemu&quot; src=&quot;http://walac.github.io/images/qemu.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And that’s it, you now can run your packet image locally!&lt;/p&gt;</content:encoded>
	<dc:date>2018-12-14T00:00:00+00:00</dc:date>
</item>
<item rdf:about="tag:blogger.com,1999:blog-2324292491075571582.post-1015693008508820138">
	<title>John Ford: Shrinking Go Binaries</title>
	<link>http://blog.johnford.org/2018/08/shrinking-go-binaries.html</link>
	<content:encoded>As part of the efforts to build a new artifact system, I wrote a CLI program to handle Taskcluster Artifact upload and download.  This is written in Go and as a result the binaries are quite large.  Since I'd like this utility to be used broadly within Mozilla CI, which requires a reasonably sized binary.  I was curious about what the various methods are and what the trade-offs of each would be.&lt;br /&gt;
&lt;br /&gt;
A bit of background is that Go binaries are static binaries which have the Go runtime and standard library built into them.  This is great if you don't care about binary size but not great if you do.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRL3x8Wrq5Ex9bc3Mo2AN6sG7PX3j2CbatE4olCl7okCWjC-aPXRd-6IgF1yXLojfBAIQ8OmpsdI06ij6c3h2UrGJ1v82TwMzYFJk8fMp95iAz_L2OnND-2tw6FB4gtS04DUHa3YK3WkA/s1600/graph.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;328&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRL3x8Wrq5Ex9bc3Mo2AN6sG7PX3j2CbatE4olCl7okCWjC-aPXRd-6IgF1yXLojfBAIQ8OmpsdI06ij6c3h2UrGJ1v82TwMzYFJk8fMp95iAz_L2OnND-2tw6FB4gtS04DUHa3YK3WkA/s640/graph.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
This graph has the binary size on the left Y axis using a linear scale in blue and the number of nanoseconds each reduction in byte takes to compute on the right Y axis using a logarithmic scale. &lt;br /&gt;
&lt;br /&gt;
To reproduce my results, you can do the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;go get -u -t -v github.com/taskcluster/taskcluster-lib-artifact-go
cd $GOPATH/src/github.com/taskcluster/taskcluster-lib-artifact-go
git checkout 6f133d8eb9ebc02cececa2af3d664c71a974e833
time (go build) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; strip ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build -ldflags=&quot;-s&quot;) &amp;amp;&amp;amp; wc -c ./artifact
time (go build -ldflags=&quot;-w&quot;) &amp;amp;&amp;amp; wc -c ./artifact
time (go build -ldflags=&quot;-s -w&quot;) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; upx -1 ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; upx -9 ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; strip ./artifact &amp;amp;&amp;amp; upx -1 ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; strip ./artifact &amp;amp;&amp;amp; upx --brute ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; strip ./artifact &amp;amp;&amp;amp; upx --ultra-brute ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
time (go build &amp;amp;&amp;amp; strip &amp;amp;&amp;amp; upx -9 ./artifact) &amp;amp;&amp;amp; wc -c ./artifact
&lt;/pre&gt;
&lt;br /&gt;
Since I was removing a lot of debugging information, I figured it'd be worthwhile checking that stack traces are still working. To ensure that I could definitely crash, I decided to panic with an error immediately on program startup.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkv-5OOxEEBwlUOjSUB5rMqQIGY3kZxOeLCmd5f0qC85n-fMbKIdOEq2qQPCG-SdcUFFx1F9-YnX-cgvHOExupDCMkJ8V9xi8sqWy1-MLi5r1PX0_2kI3Ts2Qm2I7QeEM7eJI89Om_tR8/s1600/stacks.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkv-5OOxEEBwlUOjSUB5rMqQIGY3kZxOeLCmd5f0qC85n-fMbKIdOEq2qQPCG-SdcUFFx1F9-YnX-cgvHOExupDCMkJ8V9xi8sqWy1-MLi5r1PX0_2kI3Ts2Qm2I7QeEM7eJI89Om_tR8/s640/stacks.png&quot; width=&quot;504&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Even with binary stripping and the maximum compression, I'm still able to get valid stack traces.  A reduction from 9mb to 2mb is definitely significant.  The binaries are still large, but they're much smaller than what we started out with.  I'm curious if we can apply this same configuration to other areas of the Taskcluster Go codebase with similar success, and if the reduction in size is worthwhile there.&lt;br /&gt;
&lt;br /&gt;
I think that using strip and upx -9 is probably the best path forward.  This combination provides enough of a benefit over the non-upx options that the time tradeoff is likely worth the effort.</content:encoded>
	<dc:date>2018-08-29T21:35:51+00:00</dc:date>
	<dc:creator>Unknown</dc:creator>
</item>
<item rdf:about="tag:blogger.com,1999:blog-2324292491075571582.post-3628648541656521060">
	<title>John Ford: Taskcluster Artifact API extended to support content verification and improve error detection</title>
	<link>http://blog.johnford.org/2018/08/taskcluster-artifact-api-extended-to.html</link>
	<content:encoded>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;h3&gt;
&lt;a href=&quot;http://blog.johnford.org/feeds/posts/default/-/taskcluster&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;/a&gt;
Background&lt;/h3&gt;
At Mozilla, we're developing the Taskcluster environment for doing Continuous Integration, or CI.  One of the fundamental concerns in a CI environment is being able to upload and download files created by each task execution.  We call them artifacts.  For Mozilla's Firefox project, an example of how we use artifacts is that each build of Firefox generates a product archive containing a build of Firefox, an archive containing the test files we run against the browser and an archive containing the compiler's debug symbols which can be used to generate stacks when unit tests hit an error.&lt;br /&gt;
&lt;h3&gt;
The problem &lt;/h3&gt;
In the old Artifact API, we had an endpoint which generated a signed S3 url that was given to the worker which created the artifact.  This worker could upload anything it wanted at that location.  This is not to suggest malicious usage, but that any errors or early termination of uploads could result in a corrupted artifact being stored in S3 as if it were a correct upload.&lt;br /&gt;
&lt;br /&gt;
If you created an artifact with the local contents &quot;hello-world\n&quot;, but your internet connection dropped midway through, the S3 object might only contain &quot;hello-w&quot;.  This went silent and uncaught until something much later down the pipeline (hopefully!) complained that the file it got was corrupted.  This corruption is the cause of many orange-factor bugs, but we have no way to figure out exactly where the corruption is happening.&lt;br /&gt;
&lt;br /&gt;
Our old API was also very challenging to use and artifact handling in tasks.  It would often require a task writer to use one of our client libraries to generate a Taskcluster-Signed-URL and Curl to do uploads.  For a lot of cases, this is really hazard fraught.  Curl doesn't fail on errors by default (!!!), Curl doesn't automatically handle &quot;Content-Encoding: gzip&quot; responses without &quot;Accept: gzip&quot;, which we sometimes need to serve.  It requires each user figure all of this out for themselves, each time they want to use artifacts.&lt;br /&gt;
&lt;br /&gt;
We also had a &quot;Completed Artifact&quot; pulse message which isn't actually sending anything useful.  It would send a message when the artifact is allocated in our metadata tables, not when the artifact was actually complete.  We could mark a task as being completed before all of the artifacts were finished being uploaded.  In practice, this was avoided by avoiding a call to complete the task before the uploads were done, but it was a convention.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Our solution &lt;/h3&gt;
We wanted to address a lot of issues with Taskcluster Artifacts.  Specifically the following issues are ones which we've tackled:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Corruption during upload should be detected &lt;/li&gt;
&lt;li&gt;Corruption during download should be detected&lt;/li&gt;
&lt;li&gt;Corruption of artifacts should be attributable&lt;/li&gt;
&lt;li&gt;S3 Eventual Consistency error detection &lt;/li&gt;
&lt;li&gt;Caches should be able to verify whether they are caching valid items&lt;/li&gt;
&lt;li&gt;Completed Artifact messages should only be sent when the artifact is actually complete&lt;/li&gt;
&lt;li&gt;Tasks should be unresolvable until all uploads are finished&lt;/li&gt;
&lt;li&gt;Artifacts should be really easy to use&lt;/li&gt;
&lt;li&gt;Artifacts should be able to be uploaded with browser-viewable gzip encoding &lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
Code&lt;/h4&gt;
Here's the code we wrote for this project:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;https://github.com/taskcluster/remotely-signed-s3 -- A library which wraps the S3 APIs using the lower level S3 REST Api and uses the aws4 request signing library&lt;/li&gt;
&lt;li&gt;https://github.com/taskcluster/taskcluster-lib-artifact -- A light wrapper around remotely-signed-s3 to enable JS based uploads and downloads&lt;/li&gt;
&lt;li&gt;https://github.com/taskcluster/taskcluster-lib-artifact-go -- A library and CLI written in Go&lt;/li&gt;
&lt;li&gt;https://github.com/taskcluster/taskcluster-queue/commit/6cba02804aeb05b6a5c44134dca1df1b018f1860 -- The final Queue patch to enable the new Artifact API&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
Upload Corruption&lt;/h4&gt;
If an artifact is uploaded with a different set of bytes to those which were expected, we should fail the upload.  The S3 V4 signatures system allows us to sign a request's headers, which includes an X-Amz-Content-Sha256 and Content-Length header.  This means that the request headers we get back from signing can only be used for a request which sets the X-Amz-Content-Sha256 and Content-Length to the value provided at signing.  The S3 library checks that the body of each request's Sha256 checksum matches the value provided in this header and also the Content-Length.&lt;br /&gt;
&lt;br /&gt;
The requests we get from the Taskcluster Queue can only be used to upload the exact file we asked permission to upload. This means that the only set of bytes that will allow the request(s) to S3 to complete sucessfully will be the ones we initially told the Taskcluster Queue about.&lt;br /&gt;
&lt;br /&gt;
The two main cases we're protecting against here are disk and network corruption.  The file ends up being read twice, once to hash and once to upload.  Since we have the hash calculated, we can be sure to catch corruption if the two hashes or sizes don't match.  Likewise, the possibility of network interuption or corruption is handled because the S3 server will report an error if the connection is interupted or corrupted before data matching the Sha256 hash exactly is uploaded.&lt;br /&gt;
&lt;br /&gt;
This does not protect against all broken files from being uploaded.  This is an important distinction to make.  If you upload an invalid zip file, but no corruption occurs once you pass responsibility to taskcluster-lib-artifact, we're going to happily store this defective file, but we're going to ensure that every step down the pipeline gets an exact copy of this defective file.&lt;br /&gt;
&lt;h4&gt;
Download Corruption&lt;/h4&gt;
Like corruption during upload, we could experience corruption or interruptions during downloading.  In order to combat this, we set some metadata on the artifacts in S3.  We set some extra headers during uploading:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;x-amz-meta-taskcluster-content-sha256 -- The Sha256 of the artifact passed into a library -- i.e. without our automatic gzip encoding&lt;/li&gt;
&lt;li&gt;x-amz-meta-taskcluster-content-length -- The number of bytes of the artifact passed into a library -- i.e. without our automatic gzip encoding&lt;/li&gt;
&lt;li&gt;x-amz-meta-taskcluster-transfer-sha256 -- The Sha256 of the artifact as passed over the wire to S3 servers.  In the case of identity encoding, this is the same value as x-amz-meta-taskcluster-content-sha256.  In the case of Gzip encoding, it is almost certainly not identical.&lt;/li&gt;
&lt;li&gt;x-amz-meta-taskcluster-transfer-length -- The number of bytes of the artifact as 
passed over the wire to S3 servers.  In the case of identity encoding, 
this is the same value as x-amz-meta-taskcluster-content-sha256.  In the case of Gzip encoding, it is almost certainly not identical.&lt;/li&gt;
&lt;/ol&gt;
You would be right to question whether we can trust these values once created.  The good news is that headers on S3 objects cannot be changed after upload.  These headers are also part of the S3 request signing we do on the queue.  This means that the only values which can be set are those which the Queue expects, and that they are immutable.&lt;br /&gt;
&lt;br /&gt;
Important to note is that because these are non-standard headers, verification requires explicit action on the part of the artifact downloader.  That's a big part of why we've written supported artifact downloading tools.&lt;br /&gt;
&lt;h4&gt;
Attribution of Corruption &lt;/h4&gt;
Corruption is inevitable in a massive system like Taskcluster.  What's really important is that when corruption happens we detect it and we know where to focus our remediation efforts.  In the new Artifact API, we can zero in on the culprit for corruption.&lt;br /&gt;
&lt;br /&gt;
With the old Artifact API, we don't have any way to figure out if an artifact is corrupted or where that happened.  We never know what the artifact was on the build machine, we can't verify corruption in caching systems and when we have an invalid artifact downloaded on a downstream task, we don't know whether it is invalid because the file was defective from the start or if it was because of a bad transfer.&lt;br /&gt;
&lt;br /&gt;
Now, we know that if the Sha256 checksums of the downloaded artifact, the original file was broken before it was uploaded.  We can build caching systems which ensure that the value that they're caching is valid and alert us to corruption.  We can track corruption to detect issues in our underlying infrastructure.&lt;br /&gt;
&lt;h4&gt;
Completed Artifact Messages and Task Resolution&lt;/h4&gt;
Previously, as soon as the Taskcluster Queue stored the metadata about the artifact in its internal tables and generated a signed url for the S3 object, the artifact was marked as completed.  This behaviour resulted in a slightly deceptive message being sent.  Nobody cares when this allocation occurs, but someone might care about an artifact becoming available.&lt;br /&gt;
&lt;br /&gt;
On a related theme, we also allowed tasks to be resolved before the artifacts were uploaded.  This meant that a task could be marked as &quot;Completed -- Success&quot; without actually uploading any of its artifacts.  Obviously, we would always be writing workers with the intention of avoiding this error, but having it built into the Queue gives us a stronger guarantee.&lt;br /&gt;
&lt;br /&gt;
We achieved this result by adding a new method to the flow of creating and uploading an artifact and adding a 'present' field in the Taskcluster Queue's internal Artifact table.  For those artifacts which are created atomically, and the legacy S3 artifacts, we just set the value to true.  For the new artifacts, we set it to false.  When you finish your upload, you have to run a complete artifact method.  This is sort of like a commit.&lt;br /&gt;
&lt;br /&gt;
In the complete artifact method, we verify that S3 sees the artifact as present and only once it's completed do we send the artifact completed method.  Likewise, in the complete task method, we ensure that all artifacts have a present value of true before allowing the task to complete.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
S3 Eventual Consistency and Caching Error Detection&lt;/h4&gt;
S3 works on an Eventual consistency model for some operations in some regions.  Caching systems also have a certain level of tolerance for corruption.  We're now able to determine whether the bytes we're downloading are those which we expect.  We can now rely on more than http status code to know whether the request worked.&lt;br /&gt;
&lt;br /&gt;
In both of these cases we can programmatically check if the download is corrupt and try again as appropriate.  In the future, we could even build smarts into our download libraries and tools to request caches involved to drop their data or try bypassing caches as a last result.&lt;br /&gt;
&lt;h4&gt;
Artifacts should be easy to use&lt;/h4&gt;
Right now, if your working with artifacts directly, you're probably having a hard time.  You have to use something like Curl and building urls or signed urls.  You've probably hit pitfalls like Curl not exiting with an error on a non-200 HTTP Status.  You're not getting any content verification.  Basically, it's hard.&lt;br /&gt;
&lt;br /&gt;
Taskcluster is about enabling developers to do their job effectively.  Something so critical to CI usage as artifacts should be simple to use.  To that end, we've implemented libraries for interacting with artifacts in Javascript and Go.  We've also implemented a Go based CLI for interacting with artifacts in the build system or shell scripts.&lt;br /&gt;
&lt;h5&gt;
Javascript&lt;/h5&gt;
The Javascript client uses the same remotely-signed-s3 library that the Taskcluster Queue uses internally.  It's a really simple wrapper which provides an put() and get() interface.  All of the verification of requests is handled internally, as is decompression of Gzip resources.  This was primarily written to enable integration in Docker-Worker directly.&lt;br /&gt;
&lt;h5&gt;
Go&lt;/h5&gt;
We also provide a Go library for downloading and uploading artifacts.  This is intended to be used in the Generic-Worker, which is written in Go.  The Go Library uses the minimum useful interface in the Standard I/O library for inputs and outputs.  We're also doing type assertions to do even more intelligent things on those inputs and outputs which support it.&lt;br /&gt;
&lt;h5&gt;
CLI&lt;/h5&gt;
For all other users of Artifacts, we provide a CLI tool.  This provides a simple interface to interact with artifacts.  The intention is to make it available in the path of the task execution environment, so that users can simply call &quot;artifact download --latest $taskId $name --output browser.zip.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Artifacts should allow serving to the browser in Gzip&lt;/h4&gt;
We want to enable large text files which compress extremely well with Gzip to be rendered by web browsers.  An example is displaying and transmitting logs.  Because of limitations in S3 around Content-Encoding and its complete lack of content negotiation, we have to decide when we upload an artifact whether or not it should be Gzip compressed.&lt;br /&gt;
&lt;br /&gt;
There's an option in the libraries to support automatic Gzip compression of things we're going to upload.  We chose Gzip over possibly-better encoding schemes because this is a one time choice at upload time, so we wanted to make sure that the scheme we used would be broadly implemented.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Further Improvements&lt;/h4&gt;
As always, there's still some things around artifact handling that we'd like to improve upon.  For starters, we should work on splitting artifact handling out of our Queue.  We've already agreed on a design of how we should store artifacts.  This involves splitting out all of the artifact handling out of the Queue into a different service and having the Queue track only which artifacts belong to each task run.&lt;br /&gt;
&lt;br /&gt;
We're also investigting an idea to store each artifact in the region it is created in.  Right now, all artifacts are stored in EC2's US West 2 region.  We could have a situation where a build vm and test vm are running on the same hypervisor in US East 1, but each artifact has to be upload and downloaded via US West 2.&lt;br /&gt;
&lt;br /&gt;
Another area we'd like to work on is supporting other clouds.  Taskcluster ideally supports whichever cloud provider you'd like to use.  We want to support other storage providers than S3, and splitting out the low level artifact handling gives us a huge maintainability win.&lt;br /&gt;
&lt;h4&gt;
Possible Contributions&lt;/h4&gt;
We're always open to contributions!  A great one that we'd love to see is allowing concurrency of multipart uploads in Go.  It turns out that this is a lot more complicated than I'd like it to be in order to support passing in the low level io.Reader interface.  We'd want to do some type assertions to see if the input supports io.ReaderAt, and if not, use a per-go-routine offset and file mutex to guard around seeking on the file.  I'm happy to mentor this project, so get in touch if that's something you'd like to work on. &lt;br /&gt;
&lt;h4&gt;
Conclusion&lt;/h4&gt;
This project has been a really interesting one for me.  It gave me an opportunity to learn the Go programming language and work with the underlying AWS Rest API.  It's been an interesting experience after being heads down in Node.js code and has been a great reminder of how to use static, strongly typed languages.  I'd forgotten how nice a real type system was to work with!&lt;br /&gt;
&lt;br /&gt;
Integration into our workers is still ongoing, but I wanted to give an overview of this project to keep everyone in the loop.  I'm really excited to see a reduction in the amount of corruptions for artifacts</content:encoded>
	<dc:date>2018-08-28T15:30:00+00:00</dc:date>
	<dc:creator>Unknown</dc:creator>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/08/ci-admin.html">
	<title>Dustin Mitchell: Introducing CI-Admin</title>
	<link>http://code.v.igoro.us/posts/2018/08/ci-admin.html</link>
	<content:encoded>&lt;p&gt;A major focus of recent developments in Firefox CI has been putting control of the CI process in the hands of the engineers working on the project.
For the most part, that means putting configuration &lt;a href=&quot;http://code.v.igoro.us/posts/2016/08/whats-so-special-about-in-tree.html&quot;&gt;in the source tree&lt;/a&gt;.
However, some kinds of configuration don’t fit well in the tree.
Notably, configuration of the trees themselves must reside somewhere else.&lt;/p&gt;

&lt;h3&gt;CI-Configuration&lt;/h3&gt;

&lt;p&gt;This information is collected in the &lt;a href=&quot;https://hg.mozilla.org/build/ci-configuration/file&quot;&gt;ci-configuration&lt;/a&gt; repository.
This is a code-free library containing YAML files describing various aspects of the configuration – currently the available repositories (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;projects.yml&lt;/code&gt;) and &lt;a href=&quot;http://code.v.igoro.us/posts/2018/06/actions-as-hooks.html&quot;&gt;actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This repository is designed to be easy to modify by anyone who needs to modify it, through the usual review processes.
It is even Phabricator-enabled!&lt;/p&gt;

&lt;h3&gt;CI-Admin&lt;/h3&gt;

&lt;p&gt;Historically, we’ve managed this sort of configuration by clicking around in the &lt;a href=&quot;http://tools.taskcluster.net/&quot;&gt;Taskcluster Tools&lt;/a&gt;.
The situation is analogous to clicking around in the AWS console to set up a cloud deployment – it works, and it’s quick and flexible.
But it gets harder as the configuration becomes more complex, it’s easy to make a mistake, and it’s hard to fix that mistake.
Not to mention, the tools UI shows a pretty low-level view of the situation that does not make common questions (“Is this scope available to cron jobs on the larch repo?”) easy to answer.&lt;/p&gt;

&lt;p&gt;The devops world has faced down this sort of problem, and the preferred approach is embodied in tools like Puppet or Terraform:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;write down the desired configuration in a human-parsable text files&lt;/li&gt;
  &lt;li&gt;check it into a repository and use the normal software-development processes (CI, reviews, merges..)&lt;/li&gt;
  &lt;li&gt;apply changes with a tool that enforces the desired state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “desired state” approach means that the tool examines the current configuration, compares it to the configuration expressed in the text files, and makes the necessary API calls to bring the current configuration into line with the text files.
Typically, there are utilities to show the differences, partially apply the changes, and so on.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-configuration&lt;/code&gt; repository contains those human-parsable text files.
The tool to enforce that state is &lt;a href=&quot;https://hg.mozilla.org/build/ci-admin/file&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-admin&lt;/code&gt;&lt;/a&gt;.
It has some generic resource-manipulation support, along with some very Firefox-CI-specific code to do weird things like hashing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.taskcluster.yml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Making Changes&lt;/h3&gt;

&lt;p&gt;The current process for making changes is a little cumbersome.
In part, that’s intentional: this tool controls the security boundaries we use to separate try from release, so its application needs to be carefully controlled and subject to significant human review.
But there’s also some work to do to make it easier (see below).&lt;/p&gt;

&lt;p&gt;The process is this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;make a patch to either or both repos, and get review from someone in the “Build Config - Taskgraph” module&lt;/li&gt;
  &lt;li&gt;land the patch&lt;/li&gt;
  &lt;li&gt;get someone with the proper access to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-admin apply&lt;/code&gt; for you (probably the reviewer can do this)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Future Plans&lt;/h3&gt;

&lt;h4&gt;Automation&lt;/h4&gt;

&lt;p&gt;We are in the process of setting up some automation around these repositories.
This includes Phabricator, Lando, and Treeherder integration, along with automatic unit test runs on push.&lt;/p&gt;

&lt;p&gt;More specific to this project, we also need to check that the current and expected configurations match.
This needs to happen on any push to either repo, but also in between pushes: someone might make a change “manually”, or some of the external data sources (such as the Hg access-control levels for a repo) might change without a commit to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-configuration&lt;/code&gt; repo.
We will do this via a Hook that runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-admin diff&lt;/code&gt; periodically, notifying relevant people when a difference is found.
These results, too, will appear in Treeherder.&lt;/p&gt;

&lt;h4&gt;Grants&lt;/h4&gt;

&lt;p&gt;One of the most intricate and confusing aspects of configuration for Firefox CI is the assignment of scopes to various jobs.
The current implementation uses a cascade of role inheritance and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; suffixes which, frankly, no human can comprehend.
The new plan is to “&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1465842&quot;&gt;grant&lt;/a&gt;” scopes to particular targets in a file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-configuration&lt;/code&gt;.
Each grant will have a clear purpose, with accompanying comments if necessary.
Then, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-admin&lt;/code&gt; will gather all of the grants and combine them into the appropriate role definitions.&lt;/p&gt;

&lt;h4&gt;Worker Configurations&lt;/h4&gt;

&lt;p&gt;At the moment, the configuration of, say, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-provsioner-v1/gecko-t-large&lt;/code&gt; is a bit of a mystery.
It’s visible to some people in the AWS-provisioner tool, if you know to look there.
But that definition also contains some secret data, so it is not publicly visible like roles or hooks are.&lt;/p&gt;

&lt;p&gt;In the future, we’d like to generate these configurations based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-configuration&lt;/code&gt;.
That both makes it clear how a particular worker type is configured (instance type, capacity configuration, regions, disk space, etc.), and allows anyone to propose a modification to that configuration – perhaps to try a new instance type.&lt;/p&gt;

&lt;h4&gt;Terraform Provider&lt;/h4&gt;

&lt;p&gt;As noted above, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci-admin&lt;/code&gt; is fairly specific to the needs of Firefox CI.
Other users of Taskcluster would probably want something similar, although perhaps a bit simpler.
Terraform is already a popular tool for configuring cloud services, and supports plug-in “providers”.
It would not be terribly difficult to write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform-provider-taskcluster&lt;/code&gt; that can create roles, hooks, clients, and so on.&lt;/p&gt;

&lt;p&gt;This is left as an exercise for the motivated user!&lt;/p&gt;

&lt;h3&gt;Links&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/taskcluster-config.html&quot;&gt;In-Tree Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/build/ci-configuration/&quot;&gt;ci-configuration&lt;/a&gt; / &lt;a href=&quot;https://treeherder.mozilla.org/#/jobs?repo=ci-configuration&quot;&gt;Treeherder&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/build/ci-admin/&quot;&gt;ci-admin&lt;/a&gt; / &lt;a href=&quot;https://treeherder.mozilla.org/#/jobs?repo=ci-admin&quot;&gt;Treeherder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2018-08-22T15:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/06/actions-as-hooks.html">
	<title>Dustin Mitchell: Actions as Hooks</title>
	<link>http://code.v.igoro.us/posts/2018/06/actions-as-hooks.html</link>
	<content:encoded>&lt;p&gt;You may already be familiar with &lt;a href=&quot;https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/actions.html&quot;&gt;in-tree actions&lt;/a&gt;: they allow you to do things like retrigger, backfill, and cancel Firefox-related tasks
They implement any “action” on a push that occurs after the initial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg push&lt;/code&gt; operation.&lt;/p&gt;

&lt;p&gt;This article goes into a bit of detail about how this works, and a major change we’re making to that implementation.&lt;/p&gt;

&lt;h3&gt;History&lt;/h3&gt;

&lt;p&gt;Until very recently, actions worked like this:
First, the decision task (the task that runs in response to a push and decides what builds, tests, etc. to run) creates an artifact called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actions.json&lt;/code&gt;.
This artifact contains the list of supported actions and some templates for tasks to implement those actions.
When you click an action button (in Treeherder or the Taskcluster tools, or any UI implementing the actions spec), code running in the browser renders that template and uses it to create a task, using &lt;em&gt;your&lt;/em&gt; Taskcluster credentials.&lt;/p&gt;

&lt;p&gt;I talk a lot about functionality being &lt;a href=&quot;http://code.v.igoro.us/posts/2016/08/whats-so-special-about-in-tree.html&quot;&gt;in-tree&lt;/a&gt;.
Actions are yet another example.
Actions are defined in-tree, using some pretty straightforward Python code.
That means any engineer who wants to change or add an action can do so – no need to ask permission, no need to rely on another engineer’s attention (aside from review, of course).&lt;/p&gt;

&lt;h4&gt;There’s Always a Catch: Security&lt;/h4&gt;

&lt;p&gt;Since the beginning, Taskcluster has operated on a fairly simple model: if you can accomplish something by pushing to a repository, then you can accomplish the same directly.
At Mozilla, the core source-code security model is the SCM level: try-like repositories are at level 1, project (twice) repositories at level 2, and release-train repositories (autoland, central, beta, etc.) are at level 3.
Similarly, LDAP users may have permisison to push to level 1, 2, or 3 repositories.
The current configuration of Taskcluster assigns the same scopes to users at a particular level as it does to repositories.&lt;/p&gt;

&lt;p&gt;If you have such permission, check out your scopes in the Taskcluster &lt;a href=&quot;https://tools.taskcluster.net/credentials&quot;&gt;credentials tool&lt;/a&gt; (after signing in).
You’ll see a lot of scopes there.&lt;/p&gt;

&lt;p&gt;The Release Engineering team has made release promotion an action.
This is not something that every user who can push to level-3 repository – hundreds of people – should be able to do!
Since it involves signing releases, this means that every user who can push to a level-3 repository has scopes involved in signing a Firefox release.
It’s not quite as bad as it seems: there are lots of additional safeguards in place, not least of which is the “Chain of Trust” that cryptographically verifies the origin of artifacts before signing.&lt;/p&gt;

&lt;p&gt;All the same, this is something we (and the Firefox operations security team) would like to fix.&lt;/p&gt;

&lt;p&gt;In the new model, users will not have the same scopes as the repositories they can push to.
Instead, they will have scopes to trigger specific actions on task-graphs at specific levels.
Some of those scopes will be available to everyone at that level, while others will be available only to more limited groups.
For example, release promotion would be available to the Release Management team.&lt;/p&gt;

&lt;h3&gt;Hooks&lt;/h3&gt;

&lt;p&gt;This makes actions a kind of privilege escalation: something a particular user can cause to occur, but could not do themselves.
The &lt;a href=&quot;https://docs.taskcluster.net/reference/core/taskcluster-hooks/references/api&quot;&gt;Taskcluster-Hooks&lt;/a&gt; service provides just this sort of functionality:
a hook creates a task using scopes assiged by a role, without requiring the user calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;triggerHook&lt;/code&gt; to have those scopes.
The user must merely have the appropriate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hooks:trigger-hook:..&lt;/code&gt; scope.&lt;/p&gt;

&lt;p&gt;So, we have added a “hook” kind to the action spec.
The difference from the original “task” kind is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actions.json&lt;/code&gt; specifies a hook to execute, along with well-defined inputs to that hook.
The user invoking the action must have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hooks:trigger-hook:..&lt;/code&gt; scope for the indicated hook.
We have also included some protection against clickjacking, preventing someone with permission to execute a hook being tricked into executing one maliciously.&lt;/p&gt;

&lt;h4&gt;Generic Hooks&lt;/h4&gt;

&lt;p&gt;There are three things we may wish to vary for an action:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;who can invoke the action;&lt;/li&gt;
  &lt;li&gt;the scopes with which the action executes; and&lt;/li&gt;
  &lt;li&gt;the allowable inputs to the action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these are configured within the hooks service (using &lt;a href=&quot;https://hg.mozilla.org/build/ci-admin/&quot;&gt;automation&lt;/a&gt;, of course).
If every action is configured uniquely within the hooks service, then the self-service nature of actions would be lost: any new action would require assistance from someone with permission to modify hooks.&lt;/p&gt;

&lt;p&gt;As a compromise, we noted that most actions should be available to everyone who can push to the corresponding repo, have fairly limited scopes, and need not limit their inputs.
We call these “generic” actions, and creating a new such action is self-serve.
All other actions require some kind of external configuration: allocating the scope to trigger the task, assigning additional scopes to the hook, or declaring an input schema for the hook.&lt;/p&gt;

&lt;h4&gt;Hook Configuration&lt;/h4&gt;

&lt;p&gt;The hook definition for an action hook is quite complex: it involves a complex task definition template as well as a large schema for the input to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;triggerHook&lt;/code&gt;.
For decision tasks, cron tasks, an “old” actions, this is defined in &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/.taskcluster.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.taskcluster.yml&lt;/code&gt;&lt;/a&gt;, and we wanted to continue that with hook-based actions.
But this creates a potential issue: if a push changes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.taskcluster.yml&lt;/code&gt;, that push will not automatically update the hooks – such an update requires elevated privileges and must be done by someone who can sanity-check the operation.
To solve this, &lt;a href=&quot;https://hg.mozilla.org/build/ci-admin/&quot;&gt;ci-admin&lt;/a&gt; creates tasks hooksed on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.taskcluster.yml&lt;/code&gt; it finds in any Firefox repository, naming each after a hash of the file’s content.
Thus, once a change is introduced, it can “ride the trains”, using the same hash in each repository.&lt;/p&gt;

&lt;h3&gt;Implementation and Implications&lt;/h3&gt;

&lt;p&gt;As of this writing, two common actions are operating as hooks: retrigger and backfill.
Both are “generic” actions, so the next step is to start to implement some actions that are not generic.
Ideally, nobody notices anything here: it is merely an implementation change.&lt;/p&gt;

&lt;p&gt;Once all actions have been converted to hooks, we will begin removing scopes from users.
This will have a more significant impact: lots of activities such as &lt;a href=&quot;https://tools.taskcluster.net/task-creator/&quot;&gt;manually creating tasks&lt;/a&gt; (including edit-and-create) will no longer be allowed.
We will try to balance the security issues against user convenience here.
Some common activities may be implemented as actions (such as creating loaners).
Others may be allowed as exceptions (for example, creating test tasks).
But some existing workflows may need to change to accomodate this improvement.&lt;/p&gt;

&lt;p&gt;We hope to finish the conversion process in July 2018, with that time largely taken with a slow rollout to accomodate unforseen implications.
When the project is finished, Firefox releases and other sensitive operations will be better-protected, with minimal impact to developers’ existing worflows.&lt;/p&gt;</content:encoded>
	<dc:date>2018-06-15T15:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/05/shipped-and-hosted-software.html">
	<title>Dustin Mitchell: Redeploying Taskcluster: Hosted vs. Shipped Software</title>
	<link>http://code.v.igoro.us/posts/2018/05/shipped-and-hosted-software.html</link>
	<content:encoded>&lt;p&gt;The Taskcluster team’s work on
&lt;a href=&quot;http://code.v.igoro.us/posts/2018/01/r14y.html&quot;&gt;redeployability&lt;/a&gt; means
switching from a &lt;em&gt;hosted&lt;/em&gt; service to a &lt;em&gt;shipped&lt;/em&gt; application.&lt;/p&gt;

&lt;p&gt;A hosted service is one where the authors of the software are also running the
main instance of that software. Examples include Github, Facebook, and
&lt;a href=&quot;https://mozillians.org&quot;&gt;Mozillians&lt;/a&gt;. By contrast, a shipped application is
deployed multiple times by people unrelated to the software’s authors. Examples
of shipped applications include Gitlab, Joomla, and the Rust toolchain. And, of
course, Firefox!&lt;/p&gt;

&lt;h4&gt;Hosted Services&lt;/h4&gt;

&lt;p&gt;Operating a hosted service can be liberating. Blog posts describe the joys of
&lt;a href=&quot;https://blog.github.com/2012-08-29-deploying-at-github/&quot;&gt;continuous
deployment&lt;/a&gt; – even
deploying the service multiple times per day. Bugs can be fixed quickly, either
by rolling back to a previous deployment or by deploying a fix.&lt;/p&gt;

&lt;p&gt;Deploying new features on a hosted service is pretty easy, too. Even a complex
change can be broken down into phases and accomplished without downtime. For
example, changing the backend storage for a service can be accomplished by
modifying the service to write to both old and new backends, mirroring existing
data from old to new, switching reads to the new backend, and finally removing
the code to write to the old backend. Each phase is deployed separately, with
careful monitoring.  If anything goes wrong, rollback to the old backend is
quick and easy.&lt;/p&gt;

&lt;p&gt;Hosted service developers are often involved with operation of the service, and
operational issues can frequently be diagnosed or even corrected with
modifications to the software. For example, if a service is experiencing
performance issues due to particular kinds of queries, a quick deployment to
identify and reject those queries can keep the service up, followed by a patch
to add caching or some other approach to improve performance.&lt;/p&gt;

&lt;h4&gt;Shipped Applications&lt;/h4&gt;

&lt;p&gt;A shipped application is sent out into the world to be used by other people.
Those users may or may not use the latest version, and certainly will not
update several times per day (the heroes running Firefox Nightly being a
notable exception). So, many versions of the application will be running
simultaneously. Some applications support automatic updates, but many users
want to control when – and if – they update. For example, upgrading a website
built with a CMS like Joomla is a risky operation, especially if the website
has been heavily customized.&lt;/p&gt;

&lt;p&gt;Upgrades are important both for new features and for bugfixes, including for
security bugs. An instance of an application like Gitlab might require an
immediate upgrade when a security issue is discovered. However, especially if
the deployment is several versions old, that critical upgrade may carry a great
deal of risk. Producers of shipped software sometimes provide backported fixes
for just this purpose, at least for long term support (LTS) or extended support
release (ESR) versions, but this has a substantial cost for the application
developers.&lt;/p&gt;

&lt;p&gt;Upgrading services like Gitlab or Joomla is made more difficult because there
is lots of user data that must remain accessible after the upgrade. For major
upgrades, that often requires some kind of migration as data formats and
schemas change.  In cases where the upgrade spans several major versions, it
may be necessary to apply several migrations in order. Tools like Alembic help
with this by maintaining and applying step-by-step database migrations.&lt;/p&gt;

&lt;h4&gt;Taskcluster&lt;/h4&gt;

&lt;p&gt;Today, Taskcluster is very much a hosted application. There is only one
“instance” of Taskcluster in the world, at &lt;a href=&quot;https://taskcluster.net&quot;&gt;taskcluster.net&lt;/a&gt;.
The Taskcluster team is responsible for both development and operation of the
service, and also works closely with the Firefox build team as a user of the
service.&lt;/p&gt;

&lt;p&gt;We want to make Taskcluster a shipped application. As the descriptions above
suggest, this is not a simple process. The following sections highlight some
of the challenges we are facing.&lt;/p&gt;

&lt;h5&gt;Releases and Deployment&lt;/h5&gt;

&lt;p&gt;We currently deploy Taskcluster microservices independently. That is, when we
make a change to a service like taskcluster-hooks, we deploy an upgrade to that
service without modifying the other services. We often sequence these changes
carefully to ensure continued compatibility: we expect only specific
combinations of services to run together.&lt;/p&gt;

&lt;p&gt;This is a far more intricate process than we can expect users to follow.
Instead, we will ship Taskcluster &lt;em&gt;releases&lt;/em&gt; comprised of a set of built Docker
images and a spec file identifying those images and how they should be
deployed. We will test that this particular combination of versions works well
together.&lt;/p&gt;

&lt;p&gt;Deploying a release involves combining that spec file with some
deployment-specific configuration and some infrastructure information
(implemented via &lt;a href=&quot;http://terraform.io/&quot;&gt;Terraform&lt;/a&gt;) to produce a set of
&lt;a href=&quot;http://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; resources for deployment with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;.
Kubernetes and Terraform both have limited support for migration from one
release to another: Terraform will only create or modify changed resources, and
Kubernetes will perform a phased roll-out of any modified resources.&lt;/p&gt;

&lt;p&gt;By the way, all of this build-and-release functionality is implemented in the
new &lt;a href=&quot;https://github.com/taskcluster/taskcluster-installer&quot;&gt;taskcluster-installer&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;Service Discovery&lt;/h5&gt;

&lt;p&gt;The string &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster.net&lt;/code&gt; appears quite frequently in the Taskcluster source
code. For any other deployment, that hostname is not valid – but how will the
service find the correct hostname? The question extends to determining pulse
exchange names, task artifact hostnames, and so on. There are also security
issues to consider: misconfiguration of URLs might enable XSS and CSRF attacks
from untrusted content such as task artifacts.&lt;/p&gt;

&lt;p&gt;The approach we are taking is to define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rootUrl&lt;/code&gt; from which all other URLs
and service identities can be determined. Some are determined by simple
transformations encapsulated in a new
&lt;a href=&quot;https://github.com/taskcluster/taskcluster-lib-urls&quot;&gt;taskcluster-lib-urls&lt;/a&gt;
library. Others are fetched at runtime from other services: pulse exchanges
from the taskcluster-pulse service, artifact URLs from the taskcluster-queue
service, and so on.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rootUrl&lt;/code&gt; is a single domain, with all Taskcluster services available at
sub-paths such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/queue&lt;/code&gt;. Users of the current Taskcluster installation
will note that this is a change: queue is currently at
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://queue.taskcluster.net&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://taskcluster.net/queue&lt;/code&gt;. We have
solved this issue by special-casing the rootUrl &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://taskcluster.net&lt;/code&gt; to
generate the old-style URLs. Once we have migrated all users out of the current
installation, we will remove that special-case.&lt;/p&gt;

&lt;p&gt;The single root domain is implemented using routing features supplied by
Kubernetes &lt;em&gt;Ingress&lt;/em&gt; resources, based on an HTTP proxy. This has the
side-effect that when one microservice contacts another (for example,
taskcluster-hooks calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;queue.createTask&lt;/code&gt;), it does so via the same Ingress,
a more circuitous journey than is strictly required.&lt;/p&gt;

&lt;h5&gt;Data Migrations&lt;/h5&gt;

&lt;p&gt;The first few deployments of Taskcluster will not require great support for
migrations. A staging environment, for example, can be completely destroyed and
re-created without any adverse impact.  But we will soon need to support users
upgrading Taskcluster from earlier releases with no (or at least minimal)
downtime.&lt;/p&gt;

&lt;p&gt;Our Azure tables library
(&lt;a href=&quot;https://github.com/taskcluster/azure-entities&quot;&gt;azure-entities&lt;/a&gt;) already has
rudimentary support for schema updates, so modifying the structure of table
rows is not difficult, although refactoring a single table into multiple tables
would be difficult.&lt;/p&gt;

&lt;p&gt;As we transition to using Postgres instead of Azure, we will need to adopt some
of the common migration tools. Ideally we can support downtime-free upgrades
like azure-entities does, instead of requiring downtime to run DB migrations
synchronously. &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1431783&quot;&gt;Bug 1431783&lt;/a&gt;
tracks this work.&lt;/p&gt;

&lt;h5&gt;Customization&lt;/h5&gt;

&lt;p&gt;As a former maintainer of Buildbot, I’ve had a lot of experience with CI
applications as they are used in various organizations. The surprising
observation is this: every organization thinks that their approach to CI is the
obvious and only way to do things; and every organization does things in a
radically different way. Developers gonna develop, and any CI framework &lt;em&gt;will&lt;/em&gt;
get modified to suit the needs of each user.&lt;/p&gt;

&lt;p&gt;Lots of Buildbot installations are heavily customized to meet local needs. That
has caused a lot of Buildbot users to get “stuck” at older versions, since
upgrades would conflict with the customizations. Part of this difficulty is due
to a failure of the Buildbot project to provide strong guidelines for
customization. Recent versions of Buildbot have done better by providing
clearly documented APIs and marking other interfaces as private and subject to
change.&lt;/p&gt;

&lt;p&gt;Taskcluster already has strong APIs, so we begin a step ahead. We might consider
additional guidelines:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Users should not customize existing services, except to make experimental
changes that will eventually be merged upstream.  This frees the Taskcluster
team to make changes to services without concern that those will conflict with
users’ modifications.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Users are encouraged, instead, to develop their own services, either hosted
within the Taskcluster deployment as a site-specific service, or hosted
externally but following Taskcluster API conventions. A local example is the
&lt;a href=&quot;https://github.com/mozilla/tc-coalesce&quot;&gt;tc-coalesce&lt;/a&gt; service, developed by
the release engineering team to support Mozilla-specific task-superseding
needs and hosted outside of the Taskcluster installation. On the other hand,
&lt;a href=&quot;http://github.com/taskcluster/taskcluster-stats-collector&quot;&gt;taskcluster-stats-collector&lt;/a&gt;
is deployed within the Firefox Taskcluster deployment, but is
Firefox-specific and not part of a public Taskcluster release.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While a Taskcluster release will likely encompass some pre-built worker
images for various cloud platforms, sophisticated worker deployment is the
responsibility of individual users. That may mean deploying workers to
hardware where necessary, perhaps with modifications to the build
configurations or even entirely custom-built worker implementations.  We will
provide cloud-provisioning tools that can be used to dynamically instantiate
user-specified images.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;Generated Client Libraries&lt;/h6&gt;

&lt;p&gt;The second point above raises an interesting quandry: Taskcluster uses code
generation to create its API client libraries. Historically, we have just
pushed the “latest” client to the &lt;a href=&quot;https://yarnpkg.com/en/package/taskcluster-client&quot;&gt;package
repository&lt;/a&gt; and carefully
choreographed any incompatible changes. For users who have not customized their
deployment, this is not too much trouble: any release of Taskcluster will have
a client library in the package repository corresponding to it.  We don’t have
a great way to indicate which version that is, but perhaps we will invent
something.&lt;/p&gt;

&lt;p&gt;But when Taskcluster installations are customized by adding additional
services, progress is no longer linear: each user has a distinct “fork” of the
Taskcluster API surface containing the locally-defined services. Development of
Taskcluster components poses a similar challenge: if I add a new API method to
a service, how do I call that method from another service without pushing a new
library to the package repository?&lt;/p&gt;

&lt;p&gt;The question is further complicated by the use of compiled languages. While
Python and JS clients can simply load a schema reference file at runtime (for
example, a file generated at deploy time), the Go and Java clients “bake in”
the references at compile time.&lt;/p&gt;

&lt;p&gt;Despite &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1428417&quot;&gt;much discussion&lt;/a&gt;,
we have yet to settle on a good solution for this issue.&lt;/p&gt;

&lt;h5&gt;Everything is Public!&lt;/h5&gt;

&lt;p&gt;Mozilla is Open by Design, and so is Taskcluster: with the exception of data
that must remain private (passwords, encryption keys, and material covered by
other companies’ NDAs), everything is publicly accessible. While Taskcluster
does have a sophisticated and battle-tested authorization system based on
&lt;a href=&quot;https://docs.taskcluster.net/reference/platform/taskcluster-auth/docs/scopes&quot;&gt;&lt;em&gt;scopes&lt;/em&gt;&lt;/a&gt;,
most read-only API calls do not require any scopes and thus can be made with a
simple, un-authenticated HTTP request.&lt;/p&gt;

&lt;p&gt;We take advantage of the public availability of most data by passing around
simple, authentication-free URLs. For example, the &lt;a href=&quot;https://docs.taskcluster.net/manual/using/actions/spec&quot;&gt;action
specification&lt;/a&gt;
describes downloading a decision task’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public/action.json&lt;/code&gt; artifact. Nowhere
does it mention providing any credentials to fetch the decision task, nor to
fetch the artifact itself.&lt;/p&gt;

&lt;p&gt;This is a rather fundamental design decision, and changing it would be
difficult. We might embark on that process, but we might also declare
Taskcluster an open-by-design system, and require non-OSS users to invent other
methods of hiding their data, such as firewalls and VPNs.&lt;/p&gt;

&lt;h5&gt;Transitioning from taskcluster.net&lt;/h5&gt;

&lt;p&gt;Firefox build, test, and release processes run at massive scale on the existing
Taskcluster instance at https://taskcluster.net, along with a number of smaller
Mozilla-associated projects. As we work on this “redeployability” project, we
must continue to deploy from master to that service as well – the rootUrl
special-case mentioned above is a critical part of this compatibility. We will
not be running either new or old instances from long-living Git branches.&lt;/p&gt;

&lt;p&gt;Some day, we will need to move all of these projects to a newly redeployed
cluster and delete the old. That day is still in the distant future. It will
likely involve some running of tasks in parallel to expunge any leftover
references to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster.net&lt;/code&gt;, then a planned downtime to migrate everything
over (we will want to maintain task and artifact history, for example). We will
likely finish up by redeploying a bunch of permanent redirects from
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster.net&lt;/code&gt; domains.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;That’s just a short list of some of the challenges we face in transmuting a
hosted service into a shipped application.&lt;/p&gt;

&lt;p&gt;All the while, of course, we must “keep the lights on” for the existing
deployment, and continue to meet Firefox’s needs. At the moment that includes a
project to deploy Taskcluster workers on arm64 hardware in https://packet.net,
development of the docker-engine to replace the aging &lt;a href=&quot;https://github.com/taskcluster/docker-worker&quot;&gt;docker
worker&lt;/a&gt;, using &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1415868&quot;&gt;hooks for
actions&lt;/a&gt; to reduce the
scopes afforded to level-3 users, improving taskcluster-github to support
defining decision tasks, and the usual assortment of contributed pull requests,
issue debugging, service requests.&lt;/p&gt;</content:encoded>
	<dc:date>2018-05-21T15:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/05/design-of-task-graph-generation.html">
	<title>Dustin Mitchell: Design of Task-Graph Generation</title>
	<link>http://code.v.igoro.us/posts/2018/05/design-of-task-graph-generation.html</link>
	<content:encoded>&lt;p&gt;Almost two years ago, &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1258497&quot;&gt;Bug 1258497&lt;/a&gt;
introduced a new system for generating the graph of tasks required for each
push to a Firefox source-code repository. Work continues to modify the expected
tasks and add features, but the core design is stable. Lots of Firefox
developers have encountered this system as they add or modify a job or try to
debug why a particular task is failing. So this is a good time to review the
system design at a high level.&lt;/p&gt;

&lt;p&gt;A quick note before beginning: the task-graph generation system is implemented
entirely in the Firefox source tree, and is administered as a sub-module of the
Build Config module. While it is designed to interface with Taskcluster, and
some of the authors are members of the Taskcluster team, it is not a part of
Taskcluster itself.&lt;/p&gt;

&lt;h3&gt;Requirements&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;task&lt;/em&gt; is a unit of work in the aptly-named Taskcluster platform. This might be
a Firefox build process, or a run of a chunk of a test suite. More esoteric
tasks include builds of the toolchains and OS environments used by other tasks;
cryptographic signing of Firefox installers; configuring Balrog, the service
behind Firefox’s automatic updates; and pushing APKs to the Google Play Store.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;task-graph&lt;/em&gt; is a collection of tasks linked by their dependencies. For
example, a test task cannot run until the build it is meant to test has
finished, and that build cannot run until the compiler toolchain it requires
has been built.&lt;/p&gt;

&lt;p&gt;The task-graph generation system, then, is responsible for generating a
task-graph containing the tasks required to test a try push, a landing on a
production branch, a nightly build, and a full release of Firefox. That task
graph must be minimal (for example, not rebuilding a toolchain if it has
already been built) and specific to the purpose (some tasks only run on
mozilla-central, for example).&lt;/p&gt;

&lt;p&gt;Firefox has been using some CI system – Tinderbox, then Buildbot, and now
Taskcluster – for decades, so the set of requirements is quite deep and
shrouded in historical mystery.&lt;/p&gt;

&lt;p&gt;While the resulting system may seem complex, it is a relatively simple
expression of the intricate requirements it must meet. It is also designed with
approachability in mind: many common tasks can be accomplished without fully
understanding the design.&lt;/p&gt;

&lt;h3&gt;System Design&lt;/h3&gt;

&lt;p&gt;The task-graph generation process itself runs in a task, called the &lt;em&gt;Decision
Task&lt;/em&gt;. That task is typically created in response to a push to version control,
and is typically the first task to appear in Treeherder, with a “D” symbol.
The decision task begins by checking out the pushed revision, and then runs the
task-graph generation implementation in that push. That means the system can be
tested in try, and can ride the trains just like any other change to Firefox.&lt;/p&gt;

&lt;h4&gt;Task-Graph Generation Process&lt;/h4&gt;

&lt;p&gt;The decision task proceeds in a sequence of steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Generate a graph containing all possible tasks (the &lt;em&gt;full task graph&lt;/em&gt;). As of
this writing, the full task graph contains 10,972 tasks!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Filter the graph to select the required tasks for this situation. Each
project (a.k.a. “branch” or “repo”) has different requirements. Try pushes
are a very flexible kind of filtering, selecting only the tasks indicated by
the (arcane!) try syntax or the (awesome!) try-select system (more on this
below). The result is the &lt;em&gt;target task graph&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“Optimize” the graph, by trimming unnecessary tasks. Some tasks, such as
tests, can simply be dropped if they are not required. Others, such as
toolchain builds, must be replaced by an existing task containing the
required data.  The result is the &lt;em&gt;optimized task graph&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create each of the tasks using the Taskcluster API.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The process is &lt;a href=&quot;https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/taskgraph.html#graph-generation&quot;&gt;a bit more
detailed&lt;/a&gt;
but this level of detail will do for now.&lt;/p&gt;

&lt;h4&gt;Kinds and Loaders&lt;/h4&gt;

&lt;p&gt;We’ll now focus on the first step: generating the full task graph. In an effort
to segment the mental effort required, tasks are divided into &lt;em&gt;kinds&lt;/em&gt;.  There
are some obvious kinds – build, test, toolchain – and a lot of less obvious
kinds.  Each kind has a directory in
&lt;a href=&quot;https://searchfox.org/mozilla-central/source/taskcluster/ci/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster/ci&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each kind is responsible for generating a list of tasks and their dependencies.
The tasks for all kinds are combined to make the full task graph.  Each kind
can generate its tasks in a different way; this is the job of the kind’s
&lt;em&gt;loader&lt;/em&gt;.  Each kind has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind.yml&lt;/code&gt; which points to a Python function that
acts as its loader.&lt;/p&gt;

&lt;p&gt;Most loaders just load task definitions from YAML files in the kind directory.
There are a few more esoteric loaders – for example, the test loader creates
one copy of each test for each platform, allowing a single definition of, say
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mochitest-chrome&lt;/code&gt; to run on all supported platforms.&lt;/p&gt;

&lt;h4&gt;Transforms&lt;/h4&gt;

&lt;p&gt;A “raw” task is designed for execution by a Taskcluster worker. It has all
sorts of details of the task baked into environment variables, the command to
run, routes, and so on.  We do not want to write expressions to generate that
detail over and over for each task, so we design the inputs in the YAML files
to be much more human-friendly. The system uses &lt;em&gt;transforms&lt;/em&gt; to bridge the gap:
each task output from the load is passed through a series of transforms, in the
form of Python generator functions, to produce the final, raw task.&lt;/p&gt;

&lt;p&gt;To bring some order to the process, there are some specific forms defined, with
schemas and sets of transforms to turn one into the next:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/taskcluster/taskgraph/transforms/tests.py&quot;&gt;Test Description&lt;/a&gt; -
how to perform a test, including suite and flavor, hardware features
required, chunking configuration, and so on.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/taskcluster/taskgraph/transforms/job/__init__.py&quot;&gt;Job Description&lt;/a&gt; -
how to perform a job; essentially “run Mozharness with these arguments” or
“run the Debian package-building process with these inputs”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/taskcluster/taskgraph/transforms/task.py&quot;&gt;Task Description&lt;/a&gt; -
how to build a task description; this contains all of the command
arguments, environment variables, and so on but is not specific to a
particular worker implementation.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several other “descriptions”, but these give the general idea.&lt;/p&gt;

&lt;p&gt;The final effect is that a relatively concise, readable build description like
this:&lt;/p&gt;

&lt;pre&gt;linux64/debug:
    description: &quot;Linux64 Debug&quot;
    index:
        product: firefox
        job-name: linux64-debug
    treeherder:
        platform: linux64/debug
        symbol: B
    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
    worker:
        max-run-time: 36000
    run:
        using: mozharness
        actions: [get-secrets build check-test update]
        config:
            - builds/releng_base_firefox.py
            - builds/releng_base_linux_64_builds.py
        script: &quot;mozharness/scripts/fx_desktop_build.py&quot;
        secrets: true
        custom-build-variant-cfg: debug
        tooltool-downloads: public
        need-xvfb: true
    toolchains:
        - linux64-clang
        - linux64-gcc
        - linux64-sccache
        - linux64-rust
&lt;/pre&gt;

&lt;p&gt;Can turn into a much larger task definition like
&lt;a href=&quot;https://queue.taskcluster.net/v1/task/OtYCNHmDSreSjvT8PVZWwA&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Cron&lt;/h4&gt;

&lt;p&gt;We ship “nightlies” of Firefox twice a day (making the name “nightly” a bit of
a historical artifact). This, too, is controlled in-tree, and is general enough
to support other time-based task-graphs such as Valgrind runs or Searchfox
updates.&lt;/p&gt;

&lt;p&gt;The approach is fairly simple: the &lt;a href=&quot;https://tools.taskcluster.net/hooks/project-releng/cron-task-mozilla-central&quot;&gt;hooks
service&lt;/a&gt;
creates a “cron task” for each project every 15 minutes. This task checks out
the latest revision of the project and runs a mach command that examines
&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/.cron.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cron.yml&lt;/code&gt;&lt;/a&gt; in the
root of the source tree. It then creates a decision task for each matching
entry, with a custom task-graph filter configuration to select only the desired
tasks.&lt;/p&gt;

&lt;h4&gt;Actions&lt;/h4&gt;

&lt;p&gt;For the most part, the task-graph for a push (or cron task) is defined in
advance. But developers and sheriffs often need to modify a task-graph after it
is created, for example to retrigger a task or run a test that was accidentally
omitted from a try push. Taskcluster defines a generic notion of an
&lt;a href=&quot;https://docs.taskcluster.net/manual/using/actions/spec&quot;&gt;“action”&lt;/a&gt; for just
this purpose: acting on an existing task-graph.&lt;/p&gt;

&lt;p&gt;Briefly, the decision task publishes a description of the actions that are
available for the tasks in the task-graph. Services like Treeherder and the
Taskcluster task inspector then use that description to connect user-interface
elements to those actions. When an action is executed, the user interface
creates a new task called an &lt;em&gt;action task&lt;/em&gt; that performs the desired action.&lt;/p&gt;

&lt;p&gt;Action tasks are similar to decision and cron tasks: they clone the desired
revision of the source code, then run a mach command to do whatever the user
has requested.&lt;/p&gt;

&lt;h4&gt;Multiple Projects&lt;/h4&gt;

&lt;p&gt;The task-graph generation code rides the trees, with occasional uplifts, just
like the rest of the Firefox codebase. That means that the same code must work
correctly for all branches; we do not have a different implementation for the
mozilla-beta branch, for example.&lt;/p&gt;

&lt;p&gt;While it might seem like, to run a new task on mozilla-central, you would just
land a patch adding that task on mozilla-central, it’s not that simple: without
adjusting the filtering, that task would eventually be merged to all other
projects and execute everywhere.&lt;/p&gt;

&lt;p&gt;This also makes testing tricky: since the task-graph generation is different
for every project, it’s possible to land code which works fine in try and
inbound, but fails on mozilla-central. It is easy to test task-graph generation
against specific situations (all inputs to the process are encapsulated in a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parameters.yml&lt;/code&gt; file easily downloaded from a decision task). The artistry is
in figuring out which situations to test.&lt;/p&gt;

&lt;h4&gt;Try Pushes&lt;/h4&gt;

&lt;p&gt;Pushes to try trigger decision tasks just like any other project, but the
filtering process is a little more complex.&lt;/p&gt;

&lt;p&gt;If the push comes with legacy try syntax (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-b do -p win64,linux64 -u
all[linux64-qr,windows10-64-qr] -t all[linux64-qr,windows10-64-qr]&lt;/code&gt; - clear as
mud, right?), we do our best to emulate the behavior of the Buildbot try parser
in filtering out tasks that were not requested. The legacy syntax is deeply
dependent on some Buildbot-specific features, and does not cover new
functionality like optimization, so there are lots of edge cases where it
behaves strangely or does not work at all.&lt;/p&gt;

&lt;p&gt;The better alternative is
&lt;a href=&quot;https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/try.html#try-task-config&quot;&gt;try-select&lt;/a&gt;,
where the push contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try_task_config.json&lt;/code&gt; listing exactly which tasks to
run, along with desired modifications to those tasks. The command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./mach try
fuzz&lt;/code&gt; creates such a file.  In this case, creating the target task-graph is as
simple as filtering for tasks that match the supplied list.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This has been a long post! The quote “make everything as simple as possible and
no simpler”, commonly attributed to Einstein, holds the reason – the
task-graph generation system satisfies an incredibly complex set of
requirements. In designing the system, we considered these requirements
holistically and with a deep understanding of how they developed and why they
exist, and then designed a system that was as simple as possible. The remaining
complexity is inherent to the problem it solves.&lt;/p&gt;

&lt;p&gt;The task-graph generation is covered in &lt;a href=&quot;https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/index.html&quot;&gt;the Firefox
source-docs&lt;/a&gt;
and its source is in the
&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/taskcluster/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/taskcluster&lt;/code&gt;&lt;/a&gt;
directory in the Firefox source tree.&lt;/p&gt;</content:encoded>
	<dc:date>2018-05-01T15:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/02/make-the-first-move.html">
	<title>Dustin Mitchell: Internship Applications: Make the First Move</title>
	<link>http://code.v.igoro.us/posts/2018/02/make-the-first-move.html</link>
	<content:encoded>&lt;p&gt;There’s an old story about Charles Proteus Steinmetz, a famous GE engineer in the early 20th century.
He was called to one of Henry Ford’s factories, where a huge generator was having problems that the local engineers could not solve.
After some investigation and calculation, Steinmetz made a mark on the shell of the generator and told the factory engineers to open that spot and replace the windings there.
He later sent a bill for his services to Henry Ford: $10,000.
Ford demanded an itemized bill – after all, Steinmetz had only made a single mark on the generator.
The bill came back: “Making chalk mark on generator: $1. Knowing where to make mark: $9,999.”&lt;/p&gt;

&lt;p&gt;Like electrical engineering, software development is more than just writing code.
Sometimes it can take hours to write a 3-line patch.
The hard part is knowing what patch to write.&lt;/p&gt;

&lt;p&gt;It takes time to understand the system you’re developing and the systems it interacts with.
Just undersatnding the problem you’re trying to solve can take some lengthy pondering.
There are often new programming languages involved, or new libraries or tools.
Once you start writing the code, new complications come up, and you must adjust course.&lt;/p&gt;

&lt;p&gt;Experienced software engineers can make this look easy.
They have an intuitive sense of what is important and what can be safely ignored, and for what problems might come up later.
This is probably the most important skill for newcomers to the field to work on.&lt;/p&gt;

&lt;h3&gt;Make the First Move&lt;/h3&gt;

&lt;p&gt;Lately, I’ve gotten dozens of emails from Google Summer of Code and Outreachy applicants that go like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Dear Sir,&lt;/p&gt;

  &lt;p&gt;I am interested in the Outreachy project “…”.
I have a background in JavaScript, HTML, CSS, and Java. 
Please connect me with a mentor for this project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve also seen dozens of bug comments like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I would like to work on this bug. Please guide me in what steps to take.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is nothing inherently wrong with these messages.
It’s always OK to ask for help.&lt;/p&gt;

&lt;p&gt;What’s missing is evidence that applicant has made &lt;em&gt;any&lt;/em&gt; effort to get started.
In the first case, the applicant did not even read the full project description, which indicates that the next step is to make a contribution and has links to tools for finding those contributions.
In the second case, it seems that the applicant has not even taken the first steps toward solving the bug.
In most cases, they have not even &lt;em&gt;read&lt;/em&gt; the bug!&lt;/p&gt;

&lt;p&gt;If my first instructions to an applicant are “start by reading the bug” or “taskcluster-lib-app is at https://github.com/taskcluster/taskcluster-lib-app” (something Google will happily tell you in 0.55 seconds), that suggests the applicant’s problem-solving skills need some serious work.
While GSoC and Outreachy are meant to be learning experiences, we look for applicants who are able to make the most of the experience by learning and growing on their own.
A participant who asks “what is the next step” at every step, without ever trying to figure out what steps to take, is not going to learn very much.&lt;/p&gt;

&lt;h3&gt;Advice&lt;/h3&gt;

&lt;p&gt;If you are applying to a program like Google Summer of Code or Outreachy, take the time to try to problem-solve before asking for help.
There is nothing wrong with asking for help.
But when you do, show what you have already figured out, and ask a specific question.
For example:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I would like to work on this bug.
It seems that this would require modifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster-lib-scopes&lt;/code&gt; library to add a formatter function.
I can see how this formatter would handle anyOf and allOf, but how should it format a for loop?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This comment shows that the applicant has done some thinking about the problem already, and I can see exactly where they have gotten stuck.&lt;/p&gt;</content:encoded>
	<dc:date>2018-02-23T15:00:00+00:00</dc:date>
</item>
<item rdf:about="http://code.v.igoro.us/posts/2018/01/r14y.html">
	<title>Dustin Mitchell: Taskcluster Redeployability</title>
	<link>http://code.v.igoro.us/posts/2018/01/r14y.html</link>
	<content:encoded>&lt;h3&gt;Taskcluster To Date&lt;/h3&gt;

&lt;p&gt;Taskcluster has always been open source: all of our code is on Github, and we get lots of contributions to the various repositories.
Some of our libraries and other packages have seen some use outside of a Taskcluster context, too.&lt;/p&gt;

&lt;p&gt;But today, Taskcluster is not a project that could practically be used outside of its single incarnation at Mozilla. 
For example, we hard-code the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster.net&lt;/code&gt; in a number of places, and we include our config in the source-code repositories.
There’s no legal or contractual reason someone else could not run their own Taskcluster, but it would be difficult and almost certainly break next time we made a change.&lt;/p&gt;

&lt;p&gt;The Mozilla incarnation is open to use by any Mozilla project, although our focus is obviously Firefox and Firefox-related products like Fennec.
This was a practical decision: our priority is to migrate Firefox to Taskcluster, and that is an enormous project.
Maintaining an abstract ability to deploy additional instances while working on this project was just too much work for a small team.&lt;/p&gt;

&lt;p&gt;The good news is, the focus is now shifting.
The migration from Buildbot to Taskcluster is nearly complete, and the remaining pieces are related to hardware deployment, largely by other teams.
We are returning to work on something we’ve wanted to do for a long time: support redeployability.&lt;/p&gt;

&lt;h3&gt;Redeployability&lt;/h3&gt;

&lt;p&gt;Redeployability means that Taskcluster can be deployed easily, multiple times, similar to OpenStack or Hadoop.
If, when we finish this effort, there exist several distinct “instances” of Taskcluster in the world, then we have been successful.
We will start by building a “staging” deployment of the Firefox instance, then move on to deploy instances that see production usage, first for other projects in Mozilla, and later for projects outside of Mozilla.&lt;/p&gt;

&lt;p&gt;In deciding to pursue this approach, we considered three options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Taskcluster as a service (TCaaS) – we run the single global Taskcluster instance, providing that service to other projects just like Github or Travis-CI.&lt;/li&gt;
  &lt;li&gt;Firefox CI – Taskcluster persists only as a one-off tool to support Firefox’s continuous integration system&lt;/li&gt;
  &lt;li&gt;Redeployability (redeployability) – we provide means for other projects to run dedicated Taskcluster instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TCaaS allows us to provide what we believe is a powerful platform for complex CI systems to a broad audience.
While not quite as easy to get started with, Taskcluster’s flexibility extends &lt;em&gt;far&lt;/em&gt; beyond what even a paid plan with CircleCI or Travis-CI can provide.
However, this approach would represent a new and different business realm for Mozilla.
While the organization has lots of public-facing services like MDN and Addons, other organizations do not depend on these services for production usage, nor do they pay us a fee for use of those services.
Defining and meeting SLAs, billing, support staffing, abuse response – none of these are areas of expertise within Mozilla, much less the Taskcluster team.
TCaaS would also require substantial changes to the platform itself to isolate paying customers from one another, hide confidential data, accurately meter usage, and so on.&lt;/p&gt;

&lt;p&gt;Firefox CI is, in a sense, a scaled-back, internal version of TCaaS: we provide a service, but to only one customer (Firefox Engineering).
It would mean transitioning the team to an operations focus, with little or no further development on the platform.
It would also open the doors to Firefox-specific design within Taskcluster, such as checking out the Gecko source code in the workers or sorting queued tasks by Gecko branch.
This would also shut the door to other projects such as Rust relying on Taskcluster.&lt;/p&gt;

&lt;p&gt;Redeployability represents something of a compromise between the other two options.
It allows us to make Taskcluster available outside of the narrow confines of Firefox CI without diving into a strange new business model.
We’re Mozilla – shipping open source software is right in our wheelhouse.&lt;/p&gt;

&lt;p&gt;It comes with some clear advantages, too:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Like any open-source project, users will contribute back, focusing on the parts of the system most related to their needs.
Most Taskcluster users will be medium- to large-scale engineering organizations, and thus able to dedicate the resources to design and develop significant new features.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A well-designed deployment system will help us improve operations for Firefox CI (many of our outages today are caused by deployment errors) and enable deployment by teams focused on operations.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We can deploy an entire staging &lt;em&gt;instance&lt;/em&gt; of Firefox’s Taskcluster, allowing thorough testing before deploying to production.
The current approach to staging changes is ad-hoc and differs between services, workers, and libraries.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Challenges&lt;/h3&gt;

&lt;p&gt;Of course, the redeployability project is not going to be easy.
The next few sections highlight some of the design challenges we are facing.
We have begin solving all of these and more, but as none of the solutions are set in stone I will focus just on the challenges themselves.&lt;/p&gt;

&lt;h4&gt;Deployment Process&lt;/h4&gt;

&lt;p&gt;Deploying a set of microservices and backend services like databases is pretty easy: tools like Kubernetes are designed for the purpose.
Taskcluster, however, is a little more complicated.
The system uses a number of cloud providers (packet.net, AWS, and Azure), each of which needs to be configured properly before use.&lt;/p&gt;

&lt;p&gt;Worker deployment is a complicated topic: workers must be built into images that can run in cloud services (such as AMIs), and those images must be capable of starting and contacting the Queue to fetch work without further manual input.
We already support a wide array of worker deployments on the single instance of Taskcluster, and multiple deployments would probably see an even greater diversity, so any deployment system will need to be extremely flexible.&lt;/p&gt;

&lt;p&gt;We want to use the deployment process for &lt;em&gt;all&lt;/em&gt; deployments, so it must be fast and reliable.
For example, to deploy a fix to the Secrets service, I would modify the configuration to point to the new version and initiate a full re-deploy of the Taskcluster instance.
If the deployment process causes downtime by restarting every service, or takes hours to complete, we will find ourselves “cheating” and deploying things directly.&lt;/p&gt;

&lt;h4&gt;Client Libraries&lt;/h4&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client&quot;&gt;Taskcluster&lt;/a&gt; &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client-web&quot;&gt;client&lt;/a&gt; &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client.py&quot;&gt;libraries&lt;/a&gt; contain code that is generated from the API specification for the Taskcluster services.
That means that the latest &lt;a href=&quot;https://pypi.python.org/pypi/taskcluster&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster&lt;/code&gt;&lt;/a&gt; package on PyPi corresponds to the APIs of the services as they are currently deployed.
If an instance of Taskcluster is running an older version of those services, then the newer client may not be able to call them correctly.
Likewise, an instance created for development purposes might have API methods that aren’t defined in any released version of the client libraries.&lt;/p&gt;

&lt;p&gt;A related issue is service discovery: how does a client library find the right URL for a particular service?
For platform services like the Queue and Auth, this is fairly simple, but grows more complex for services which might be deployed several times, such as the AWS provisioner.&lt;/p&gt;

&lt;h4&gt;Configuration and Customization&lt;/h4&gt;

&lt;p&gt;No two deployments of Taskcluster will be exactly alike – that would defeat the purpose.
We must support a limited amount of flexibility: which services are enabled, what features of those services are enabled, and credentials for the various cloud services we use.&lt;/p&gt;

&lt;p&gt;In some cases the configuration for a service relies on values derived from another service that must already be started.
For example, the Queue needs Taskcluster credentials generated by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createTask&lt;/code&gt; on a running Auth service.&lt;/p&gt;

&lt;h4&gt;Upgrades&lt;/h4&gt;

&lt;p&gt;Many of the new features we have added in Taskcluster have been deployed through a carefully-choreographed, manual process.
For example, to deploy parameterized roles support, which involved a change to the Auth sevice’s backend support, I disabled writes to the backend, carefully copied the data to the new backend, then landed a patch to begin using the new backend with the old frontend, and so on.
We cannot expect users to follow hand-written instructions for such delicate dances.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;The Taskcluster team has a lot of work to do.
But this is a direction many of us have been itching to move for several years now, so we are eagerly jumping into it.
Look for more updates on the redeployability project in the coming months!&lt;/p&gt;</content:encoded>
	<dc:date>2018-01-19T15:00:00+00:00</dc:date>
</item>
<item rdf:about="https://ckousik.github.io/gsoc/2017/07/19/Final-Work-Product">
	<title>Chinmay Kousik: Livelog Proxy(WebhookTunnel): Final Work Product</title>
	<link>https://ckousik.github.io/gsoc/2017/07/19/Final-Work-Product.html</link>
	<content:encoded>&lt;p&gt;The project was initially named Livelog Proxy, but during the community bonding period was renamed as Webhooktunnel, as it
more accurately captured the full scope of the project.The Webhooktunnel repository can be found &lt;a href=&quot;https://github.com/taskcluster/webhooktunnel&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;tasks-completed&quot;&gt;Tasks Completed:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;[x] Main webhooktunnel &lt;a href=&quot;https://github.com/taskcluster/webhooktunnel&quot;&gt;project&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;[x] &lt;a href=&quot;https://github.com/taskcluster/taskcluster-auth/pull/107&quot;&gt;Taskcluster Auth integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[x] Deployment to docker cloud&lt;/li&gt;
  &lt;li&gt;[x] &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/pull/301&quot;&gt;taskcluster-worker integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[x] &lt;a href=&quot;https://github.com/taskcluster/docker-worker/pull/304&quot;&gt;docker-worker integration&lt;/a&gt; [Stretch Goal]&lt;/li&gt;
  &lt;li&gt;[ ] generic-worker integration [Stretch Goal]&lt;/li&gt;
  &lt;li&gt;[ ] Routing DHT [Stretch Goal]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;webhooktunnel-details&quot;&gt;Webhooktunnel Details:&lt;/h3&gt;
&lt;p&gt;Webhooktunnel works by multiplexing HTTP requests over a WebSocket connection. This allows clients to connect to the proxy and 
server webhooks over the websocket connection instead of exposing a port to the internet.&lt;/p&gt;

&lt;p&gt;The connection process for clients(workers) is explained in the diagram below:&lt;/p&gt;
&lt;div id=&quot;whtunnel-connection&quot;&gt;&lt;/div&gt;
&lt;p&gt;The client(worker) needs an ID and JWT to connect to the proxy. These are supplied by &lt;a href=&quot;https://github.com/taskcluster/taskcluster-auth&quot;&gt;tc-auth&lt;/a&gt;.
The proxy(whtunnel) responds by upgrading the HTTP(S) connection to a websocket connection and supplies the client’s base URL in a response header.&lt;/p&gt;

&lt;p&gt;An example of request forwarding works as follows:&lt;/p&gt;
&lt;div id=&quot;whtunnel-request-forwarding&quot;&gt;&lt;/div&gt;

&lt;p&gt;Webhooktunnel can also function as a websocket proxy.&lt;/p&gt;
&lt;div id=&quot;whtunnel-wsbridge&quot;&gt;&lt;/div&gt;

&lt;p&gt;Webhooktunnel has already been integrated into Taskcluster worker and is used for serving livelogs from task builds.&lt;/p&gt;

&lt;p&gt;The core of Webhooktunnel is the multiplexing library &lt;a href=&quot;https://github.com/taskcluster/webhooktunnel/tree/master/wsmux&quot;&gt;wsmux&lt;/a&gt;.
Wsmux allows creating client and server sessions over a WebSocket connection and creates multiplexed streams over the 
connection. These streams are exposed using a &lt;code class=&quot;highlighter-rouge&quot;&gt;net.Conn&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Webhooktunnel also consists of a &lt;a href=&quot;https://github.com/taskcluster/webhooktunnel/tree/master/cmd/whclient&quot;&gt;command line client&lt;/a&gt;, 
which can forward incoming connections from the proxy to a local port. 
This is useful as it can be used by servers which are behind a NAT/Firewall.&lt;/p&gt;</content:encoded>
	<dc:date>2017-07-19T17:00:00+00:00</dc:date>
</item>
<item rdf:about="https://ckousik.github.io/gsoc/2017/06/16/WebSocket-Multiplexer-Overview">
	<title>Chinmay Kousik: WebSocket Multiplexer Overview</title>
	<link>https://ckousik.github.io/gsoc/2017/06/16/WebSocket-Multiplexer-Overview.html</link>
	<content:encoded>&lt;h3 id=&quot;general-idea&quot;&gt;General Idea&lt;/h3&gt;
&lt;p&gt;WebSocket multpilexer enables creation of multiple tcp-like streams over a WebSocket connection. Since each stream can be treated as a separate &lt;code class=&quot;highlighter-rouge&quot;&gt;net.Conn&lt;/code&gt; instance, it is used by other components to proxy HTTP requests. A new stream can be opened for each request, and they can be handled in a manner identical to tcp streams. Wsmux contains two components: Sessions and Streams. Sessions wrap WebSocket connections and allow management of streams over the connection. Session implements &lt;code class=&quot;highlighter-rouge&quot;&gt;net.Listener&lt;/code&gt;, and can be used by an &lt;code class=&quot;highlighter-rouge&quot;&gt;http.Server&lt;/code&gt; instance to serve requests. Streams are the interface which allow users to send and receive multiplexed data. Streams implement &lt;code class=&quot;highlighter-rouge&quot;&gt;net.Conn&lt;/code&gt;. Streams have internal mechanisms for buffering and congestion control.&lt;/p&gt;

&lt;h3 id=&quot;why-websocket&quot;&gt;Why WebSocket?&lt;/h3&gt;
&lt;p&gt;The decision to use WebSocket (&lt;code class=&quot;highlighter-rouge&quot;&gt;github.com/gorilla/websocket&lt;/code&gt;) instead of supporting a &lt;code class=&quot;highlighter-rouge&quot;&gt;net.Conn&lt;/code&gt; was made for the following reasons:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;WebSocket handshakes can be used for intitiating a connection instead of writing a custom handshake. Wsmux can be used as a subprotocol in the WebSocket handshake. This greatly simplifies the implementation of wsmux.&lt;/li&gt;
  &lt;li&gt;WebSocket convenience mathods (&lt;code class=&quot;highlighter-rouge&quot;&gt;ReadMessage&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;WriteMessage&lt;/code&gt;) simplify sending and receiving of data and control frames.&lt;/li&gt;
  &lt;li&gt;Control messages such as ping, pong, and close need not be implemented separately in wsmux. WebSocket control frames can be used for this purpose.&lt;/li&gt;
  &lt;li&gt;Adding another layer of abstraction over WebSocket enables connections to be half-closed. WebSocket does not allow for half closed connections, but wsmux streams can be half closed, thus simplifying usage.&lt;/li&gt;
  &lt;li&gt;Since WebSocket frames already contain the length of the message, the length field can be dropped from wsmux frames. This reduces the size of the wsmux header to 5 bytes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;framing&quot;&gt;Framing&lt;/h3&gt;
&lt;p&gt;WebSocket multiplexer implements a very simple framing technique. The total header size is 5 bytes.&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[ message type - 8 bits ][ stream ID - 32 bits ]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Messages can have the following types:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;msgSYN: Used to initiate a connection.&lt;/li&gt;
  &lt;li&gt;msgACK: Used to acknowledge bytes read on a connection.&lt;/li&gt;
  &lt;li&gt;msgDAT: Signals that data is being sent.&lt;/li&gt;
  &lt;li&gt;msgFIN: Signals stream closure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sessions&quot;&gt;Sessions&lt;/h3&gt;
&lt;p&gt;A Session wraps a WebSocket connection and enables usage of the wsmux subprotocol. A Session is typically used to manage the various wsmux streams. Sessions are of two types:Server, Client. The only difference between a Server Session and a Client Session is that the ID of a stream created by a Server Session will be even numbered while the ID of a stream created by a Client will be odd numbered. A connection must have only one Server and one Client. Sessions read messages from the WebSocket connection and forward the data to the appropriate stream. Streams are responsible for buffering and framing of data. Streams must send data by invoking the &lt;code class=&quot;highlighter-rouge&quot;&gt;send&lt;/code&gt; method of their Session.&lt;/p&gt;

&lt;h3 id=&quot;streams&quot;&gt;Streams&lt;/h3&gt;
&lt;p&gt;Streams allow users to interface with data tagged with a particular ID, called the stream ID. Streams contain circular buffers for congestion control and are also responsible for generating and sending msgACK frames to the remote session whenever data is read. Streams handle framing of data when data is being sent, and also allow setting of deadlines for &lt;code class=&quot;highlighter-rouge&quot;&gt;Read&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Write&lt;/code&gt; calls. Internally, streams are represented using a Finite State Machine, which has been described in a &lt;a href=&quot;https://ckousik.github.io/gsoc/2017/05/16/Stream-States-Part-1.html&quot;&gt;previous blog post&lt;/a&gt;. Performance metrics for streams have also been measured and are availabe &lt;a href=&quot;https://ckousik.github.io/gsoc/2017/05/24/Stream-Metrics.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Wsmux is being used by the two major components of Webhooktunnel: Webhook Proxy, and Webhook Client. It has been demonstrated that wsmux can be used to multiplex HTTP requests reliably, and can also support WebSocket over wsmux streams.&lt;/p&gt;

&lt;p&gt;The repository can be found &lt;a href=&quot;https://github.com/taskcluster/webhooktunnel/tree/master/wsmux&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2017-06-16T14:30:00+00:00</dc:date>
</item>
<item rdf:about="https://ckousik.github.io/gsoc/2017/05/24/Stream-Metrics">
	<title>Chinmay Kousik: Stream Metrics</title>
	<link>https://ckousik.github.io/gsoc/2017/05/24/Stream-Metrics.html</link>
	<content:encoded>&lt;p&gt;In the previous post, I gave a brief explanation of how the stream has been refactored to resemble a finite state machine. This post elaborates on the performance metrics of streams based on buffer size and number of concurrent streams.&lt;/p&gt;
&lt;h3 id=&quot;buffers&quot;&gt;Buffers&lt;/h3&gt;
&lt;p&gt;Each stream has an internal buffer which is used to store data which is sent to it from the remote side. The default buffer size is 1024 bytes as of now. The buffer size is immutable and cannot be changed once the stream has been created. The buffer is internally implemented as a circular queue of bytes, and implements &lt;code class=&quot;highlighter-rouge&quot;&gt;io.ReadWriter&lt;/code&gt;. When a stream is created, the stream assumes the remote buffer capacity to be zero. When the stream is accepted, the remote connection informs the stream of its buffer size and the remote buffer capacity is updated. Streams are setup to track remote capacity, unblocking bytes when an ACK frame arrives, and reducing remote capacity when a certain number of bytes are written. Streams can only write as many bytes as remote capacity, and will block writes until further bytes are unblocked. Thus, buffer size has a significant effect on performance.&lt;br /&gt;
The following plot shows the time taken for a &lt;code class=&quot;highlighter-rouge&quot;&gt;Write()&lt;/code&gt; call over 100 concurrent streams as a function of buffer size.&lt;/p&gt;



&lt;p&gt;1500 bytes are sent and echoed back over each stream. It is clear that the time taken reduces exponentially as a function of buffer size. This is because smaller buffers require more messages to be sent over the websocket connection. A stream with a 1024 byte buffer needs to exchange a minimum of 3 messages for the data to be completely sent to the remote connection: write 1024 bytes, receive ACK &amp;gt;= 476 bytes, write 476 bytes. A stream with a large enough buffer can write data using a single message. The intended buffer size is 4k.&lt;/p&gt;

&lt;h3 id=&quot;concurrent-streams&quot;&gt;Concurrent Streams&lt;/h3&gt;
&lt;p&gt;Each session is capabale of handling concurrent streams. This test keeps the buffer size constant as 1024 bytes and varies the number of concurrent streams.&lt;br /&gt;
The following plot describes the time taken to echo 1500 bytes over each stream with a buffer size of 1k as a function of number of concurrent streams:&lt;/p&gt;



&lt;p&gt;It is simple to fit a quadratic curve to this curve. A reason for this could be a limit on the throughput of the websocket connection.&lt;/p&gt;</content:encoded>
	<dc:date>2017-05-24T14:30:00+00:00</dc:date>
</item>
<item rdf:about="https://ckousik.github.io/gsoc/2017/05/16/Stream-States-Part-1">
	<title>Chinmay Kousik: Stream States Part 1</title>
	<link>https://ckousik.github.io/gsoc/2017/05/16/Stream-States-Part-1.html</link>
	<content:encoded>&lt;p&gt;Streams can be modelled as an FSM by determining the different states a stream can be in and all valid state transistions. Initially, a stream is in the &lt;code class=&quot;highlighter-rouge&quot;&gt;created&lt;/code&gt; state.
This state signifies that the stream has been created. This is possible in two different ways: a) The stream was created by the session, added to the stream map, and a SYN
packet with the stream’s ID was sent to the remote session, or b) A SYN packet was received from the remote session and a new stream was created and added to the stream map.
In case of (a) the stream waits for an ACK packet from the remote session and as soon as the ACK packet arrives, it transistions to the &lt;code class=&quot;highlighter-rouge&quot;&gt;accepted&lt;/code&gt; state. In case of (b) the 
session sends an ACK packet, and the the stream transitions to the &lt;code class=&quot;highlighter-rouge&quot;&gt;accepted&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;Once in the &lt;code class=&quot;highlighter-rouge&quot;&gt;accepted&lt;/code&gt; state the stream can read and write from the stream. When a DAT packet arrives, the data is push to the stream’s buffer. When data is read out of the
buffer using a &lt;code class=&quot;highlighter-rouge&quot;&gt;Read()&lt;/code&gt; call, an ACK packet is sent to the remote stream with the number of bytes read. When an ACK packet is received in the accepted state, the number of 
bytes unblocked (the number of bytes the remote session is willing to accept), is updated. If the stream is closed by a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;Close()&lt;/code&gt;, then the stream transitions to the 
&lt;code class=&quot;highlighter-rouge&quot;&gt;closed&lt;/code&gt; state and sends a FIN packet to the remote stream. When a FIN packet is received, the stream transitions to the &lt;code class=&quot;highlighter-rouge&quot;&gt;remoteClosed&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;highlighter-rouge&quot;&gt;closed&lt;/code&gt; state, the stream can not write any data to the remote connection. All &lt;code class=&quot;highlighter-rouge&quot;&gt;Write()&lt;/code&gt; calls return an ErrBrokenPipe error. The stream can still receive data, and canread data from the buffer.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;remoteClosed&lt;/code&gt; state signifies that the remote stream will not send any more data to the stream. &lt;code class=&quot;highlighter-rouge&quot;&gt;Read()&lt;/code&gt; calls can still read data from the buffer. If the buffer is empty
then the &lt;code class=&quot;highlighter-rouge&quot;&gt;Read()&lt;/code&gt; calls return EOF. The stream can write data to the remote session.&lt;/p&gt;

&lt;p&gt;If a FIN packet is received when in the &lt;code class=&quot;highlighter-rouge&quot;&gt;closed&lt;/code&gt; state, or &lt;code class=&quot;highlighter-rouge&quot;&gt;Close()&lt;/code&gt; is called in the &lt;code class=&quot;highlighter-rouge&quot;&gt;remoteClosed&lt;/code&gt; state, the stream transitions to the &lt;code class=&quot;highlighter-rouge&quot;&gt;dead&lt;/code&gt; state. All &lt;code class=&quot;highlighter-rouge&quot;&gt;Write()&lt;/code&gt; calls fail
in the &lt;code class=&quot;highlighter-rouge&quot;&gt;dead&lt;/code&gt; state, but &lt;code class=&quot;highlighter-rouge&quot;&gt;Read()&lt;/code&gt; can retreive data from the buffer. If the stream is in the &lt;code class=&quot;highlighter-rouge&quot;&gt;dead&lt;/code&gt; state, and the buffer is empty, then the stream is removed by its Session.&lt;/p&gt;

&lt;p&gt;The state transitions can be summed up in the following diagram:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;stream states&quot; src=&quot;https://ckousik.github.io/img/streamstates.jpg&quot; /&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2017-05-16T17:00:00+00:00</dc:date>
</item>
<item rdf:about="https://ckousik.github.io/gsoc/2017/05/08/Webhook-Tunnel">
	<title>Chinmay Kousik: GSOC Project: Webhook Tunnel</title>
	<link>https://ckousik.github.io/gsoc/2017/05/08/Webhook-Tunnel.html</link>
	<content:encoded>&lt;p&gt;I got accepted to Google Summer of Code (GSoC) 2017. I will be working with Mozilla Taskcluster, and my project is Webhook Tunnel (we changed the name from livelog proxy).
TaskCluster workers are hosted on services such as EC2 and currently expose ports to the internet and allows clients to call API endpoints. 
This may not be feasible in a data center setup. Webhook proxy aims to mitigate this problem by allowing workers to connect to a proxy (part of webhook tunnel) over an 
outgoing WebSocket connection and the proxy in turn exposes API endpoints to the internet. This is implemented as a distributed system for handling high loads.&lt;/p&gt;

&lt;p&gt;This is similar to ngrok, or localtunnel, but a key difference is that instead of providing a port that clients can connect to, webhook tunnel exposes APIs as
“&amp;lt;worker-id&amp;gt;.taskcluster-proxy.net/&amp;lt;endpoint&amp;gt;”. This is a much more secure way of exposing endpoints.&lt;/p&gt;

&lt;p&gt;The initial plan is to deploy this on Docker Cloud. Details will follow in further posts.&lt;/p&gt;</content:encoded>
	<dc:date>2017-05-08T16:01:00+00:00</dc:date>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5379">
	<title>Selena Deckelmann: TaskCluster 2016Q2 Retrospective</title>
	<link>https://www.chesnok.com/daily/2016/08/03/taskcluster-2016q2-retrospective/</link>
	<content:encoded>&lt;p&gt;The TaskCluster Platform team worked very hard in Q2 to support the migration off Buildbot, bring new projects into our CI system and look forward with experiments that might enable fully-automated VM deployment on hardware in the future.&lt;/p&gt;
&lt;p&gt;We also brought on 5 interns. For a team of 8 engineers and one manager, this was a tremendous team accomplishment. We are also working closely with interns on the Engineering Productivity and Release Engineering teams, resulting in a much higher communication volume than in months past.&lt;/p&gt;
&lt;p&gt;We continued our work with RelOps to land Windows builds, and those are available in pushes to Try. This means people can use “one click loaners” for Windows builds as well as Linux (through the Inspect Task link for jobs)! Work on Windows tests is proceeding.&lt;/p&gt;
&lt;p&gt;We also created try pushes for Mac OS X tests, and integrated them with the Mac OS X cross-compiled builds. This also meant deep diving into the cross-compiled builds to green them up in Q3 after some compiler changes.&lt;/p&gt;
&lt;p&gt;A big part of the work for our team and for RelEng was preparing to implement a new kind of signing process. Aki and Jonas spent a good deal of time on this, as did many other people across PlatformOps. What came out of that work was a detailed specification for TaskCluster changes and for a new service from RelEng. We expect to see prototypes of these ideas by the end of August, and the major blocking changes to the workers and provisioner to be complete then too.&lt;/p&gt;
&lt;p&gt;This all leads to being able to ship Linux Nightlies directly from TaskCluster by the end of Q3. We’re optimistic that this is possible, with the knowledge that there are still a few unknowns and a lot has to come together at the right time.&lt;/p&gt;
&lt;p&gt;Much of the work on TaskCluster is like building a 747 in-flight. The microservices architecture enables us to ship small changes quickly and without much pre-arranged coordination. As time as gone on, we have consolidated some services (the scheduler is deprecated in favor of the “big graph” scheduling done directly in the queue), separated others (we’ve moved Treeherder-specific services into its own component, and are working to deprecate mozilla-taskcluster in favor of a taskcluster-hg component), and refactored key parts of our systems (intree scheduling last quarter was an important change for usability going forward). This kind of change is starting to slow down as the software and the team adapts and matures.&lt;/p&gt;
&lt;p&gt;I can’t wait to see what this team accomplishes in Q3!&lt;/p&gt;
&lt;p&gt;Below is the team’s partial list of accomplishments and changes. Please drop by #taskcluster or drop an email to our tools-taskcluster  lists.mozilla.org mailing list with questions or comments!&lt;/p&gt;
&lt;h3&gt;Things we did this quarter:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;initial investigation and timing data around using sccache for linux builds&lt;/li&gt;
&lt;li&gt;released update for sccache to allow working in a more modern python environment&lt;/li&gt;
&lt;li&gt;created taskcluster managed s3 buckets with appropriate policies&lt;/li&gt;
&lt;li&gt;tested linux builds with patched version of sccache&lt;/li&gt;
&lt;li&gt;tested docker-worker on packet.net for on hardware testing&lt;/li&gt;
&lt;li&gt;worked with jmaher on talos testing with docker-worker on releng hardware&lt;/li&gt;
&lt;li&gt;created livelog plugin for taskcluster-worker (just requires tests now)&lt;/li&gt;
&lt;li&gt;added reclaim logic to taskcluster-worker&lt;/li&gt;
&lt;li&gt;converted gecko and gaia in-tree tasks to use new v2 treeherder routes&lt;/li&gt;
&lt;li&gt;Updated gaia-taskcluster to allow github repos to use new taskcluster-treeherder reporting&lt;/li&gt;
&lt;li&gt;move docs, schemas, references to https&lt;/li&gt;
&lt;li&gt;refactor documentation site into tutorial / manual / reference&lt;/li&gt;
&lt;li&gt;add READMEs to reference docs&lt;/li&gt;
&lt;li&gt;switch from a * certificate to a SAN certificate for taskcluster.net&lt;/li&gt;
&lt;li&gt;increase accessibility of AWS provisioner by separating bar-graph stuff from workerType configuration&lt;/li&gt;
&lt;li&gt;use roles for workerTypes in the AWS provisioner, instead of directly specifying scopes&lt;/li&gt;
&lt;li&gt;allow non-employees to login with Okta, improve authentication experience&lt;/li&gt;
&lt;li&gt;named temporary credentials&lt;/li&gt;
&lt;li&gt;use npm shrinkwrap everywhere&lt;/li&gt;
&lt;li&gt;enable coalescing&lt;/li&gt;
&lt;li&gt;reduce the artifact retention time for try jobs (to reduce S3 usage)&lt;/li&gt;
&lt;li&gt;support retriggering via the treeherder API&lt;/li&gt;
&lt;li&gt;document azure-entities&lt;/li&gt;
&lt;li&gt;start using queue dependencies (big-graph-scheduler)&lt;/li&gt;
&lt;li&gt;worked with NSS team to have tasks scheduled and displayed within treeherder&lt;/li&gt;
&lt;li&gt;Improve information within docker-worker live logs to include environment information (ip address, instance type, etc)&lt;/li&gt;
&lt;li&gt;added hg fingerprint verification to decision task&lt;/li&gt;
&lt;li&gt;Responded and deployed patches to security incidents discovered in q2 &lt;/li&gt;
&lt;li&gt;taskcluster-stats-collector running with signalfx&lt;/li&gt;
&lt;li&gt;most major services using signalfx and sentry via new monitoring library taskcluster-lib-monitor&lt;/li&gt;
&lt;li&gt;Experimented with QEMU/KVM and libvirt for powering a taskcluster-worker engine&lt;/li&gt;
&lt;li&gt;QEMU/KVM engine for taskcluster-worker&lt;/li&gt;
&lt;li&gt;Implemented Task Group Inspector&lt;/li&gt;
&lt;li&gt;Organized efforts around front-end tooling&lt;/li&gt;
&lt;li&gt;Re-wrote and generalized the build process for taskcluster-tools and future front-end sites&lt;/li&gt;
&lt;li&gt;Created the Migration Dashboard&lt;/li&gt;
&lt;li&gt;Organized efforts with contractors to redesign and improve the UX of the taskcluster-tools site&lt;/li&gt;
&lt;li&gt;First Windows tasks in production – NSS builds running on Windows 2012 R2 &lt;/li&gt;
&lt;li&gt;Windows Firefox desktop builds running in production (currently shown on staging treeherder)&lt;/li&gt;
&lt;li&gt;new features in generic worker (worker type metadata, retaining task users/directories, managing secrets in secrets store, custom drive for user directories, installing as a startup item rather than service, improved syscall integration for logins and executing processes as different users)&lt;/li&gt;
&lt;li&gt;many firefox desktop build fixes including fixes to python build scripts, mozconfigs, mozharness scripts and configs&lt;/li&gt;
&lt;li&gt;CI cleanup https://travis-ci.org/taskcluster&lt;/li&gt;
&lt;li&gt;support for relative definitions in jsonschema2go&lt;/li&gt;
&lt;li&gt;schema/references cleanup&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Paying down technical debt&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fixed numerous issues/requests within mozilla-taskcluster&lt;/li&gt;
&lt;li&gt;properly schedule and retrigger tasks using new task dependency system&lt;/li&gt;
&lt;li&gt;add more supported repositories&lt;/li&gt;
&lt;li&gt;Align job state between treeherder and taskcluster better (i.e cancels)&lt;/li&gt;
&lt;li&gt;Add support for additional platform collection labels (pgo/asan/etc)&lt;/li&gt;
&lt;li&gt;fixed retriggering of github tasks in treeherder &lt;/li&gt;
&lt;li&gt;Reduced space usage on workers using docker-worker by removing temporary images &lt;/li&gt;
&lt;li&gt;fixed issues with gaia decision task that prevented it from running since March 30th.&lt;/li&gt;
&lt;li&gt;Improved robustness of image creation image&lt;/li&gt;
&lt;li&gt;Fixed all linter issues for taskcluster-queue&lt;/li&gt;
&lt;li&gt;finished rolling out shrinkwrap to all of our services&lt;/li&gt;
&lt;li&gt;began trial of having travis publish our libraries (rolled out to 2 libraries now. talking to npm to fix a bug for a 3rd)&lt;/li&gt;
&lt;li&gt;turned on greenkeeper everywhere then turned it off again for the most part (it doesn’t work with shrinkwrap, etc)&lt;/li&gt;
&lt;li&gt;“modernized” (newer node, lib-loader, newest config, directory structure, etc) most of our major services&lt;/li&gt;
&lt;li&gt;fix a lot of subtle background bugs in tc-gh and improve logging&lt;/li&gt;
&lt;li&gt;shared eslint and babel configs created and used in most services/libraries&lt;/li&gt;
&lt;li&gt;instrumented taskcluster-queue with statistics and error reporting&lt;/li&gt;
&lt;li&gt;fixed issue where task dependency resolver would hang&lt;/li&gt;
&lt;li&gt;Improved error message rendering on taskcluster-tools&lt;/li&gt;
&lt;li&gt;Web notifications for one-click-loaner UI on taskcluster-tools&lt;/li&gt;
&lt;li&gt;Migrated stateless-dns server from tutum.co to docker cloud&lt;/li&gt;
&lt;li&gt;Moved provisioner off azure storage development account&lt;/li&gt;
&lt;li&gt;Moved our npm package to a single npm organization&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2016-08-03T19:56:19+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="https://walac.github.io/taskcluster-worker-macosx-engine/">
	<title>Wander Lairson Costa: The taskcluster-worker Mac OSX engine</title>
	<link>https://walac.github.io/taskcluster-worker-macosx-engine/</link>
	<content:encoded>&lt;p&gt;In this quarter, I worked on implementing the
&lt;a href=&quot;https://blog.gregarndt.com/taskcluster/2016/03/24/birth-of-new-worker/&quot;&gt;taskcluster-worker&lt;/a&gt;
Mac OSX engine. Before talking about this specific implementation,
let me explain what a worker is and how taskcluster-worker differs from
&lt;a href=&quot;https://github.com/taskcluster/docker-worker&quot;&gt;docker-worker&lt;/a&gt;, the currently
main worker in
&lt;a href=&quot;https://yourdomain.com/mozilla,%20ci/2014/03/04/taskcluster.html&quot;&gt;Taskcluster&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;The role of a Taskcluster worker&lt;/h3&gt;

&lt;p&gt;When a user submits a task graph to Taskcluster,
contrary to the common sense (at least if you are used on how OSes
schedulers usually work), these tasks are submitted to the scheduler first,
which is responsible to process dependencies and enqueue them. In the
&lt;a href=&quot;https://docs.taskcluster.net/manual&quot;&gt;Taskcluster manual page&lt;/a&gt; there is a
clear picture ilustrating this concept.&lt;/p&gt;

&lt;p&gt;The provisioner is responsible for looking at the queue and determine how
many pending tasks exist and, based on that, it launches worker instances to
run these tasks.&lt;/p&gt;

&lt;p&gt;Then comes the figure of the worker. The worker is responsible for actually
executing the task. It claims a task from the queue, runs it, upload the
generated artifacts and submits the status of the finished task, using the
&lt;a href=&quot;https://docs.taskcluster.net/manual/apis&quot;&gt;Taskcluster APIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-worker&lt;/code&gt; is a worker that runs task command inside a docker container.
The task payload specifies a &lt;a href=&quot;https://www.docker.com/what-docker&quot;&gt;docker&lt;/a&gt;
image as well as a command line to run, among other environment parameters.
docker-worker pulls the specified docker image and runs task commands inside it.&lt;/p&gt;

&lt;h3&gt;taskcluster-worker and the OSX engine&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster-worker&lt;/code&gt; is a generic and modularized worker under active
development by the Taskcluster team. The worker delegates the task execution
to one of the available
&lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/tree/master/engines&quot;&gt;engines&lt;/a&gt;.
An engine is a component of taskcluster-worker responsible for running a task
under a specific system environment. Other features, like environment variable
setting, live logging, artifact uploading, etc., are handled by
&lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/tree/master/plugins&quot;&gt;worker plugins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am implementing the Mac OSX engine, which will mainly be used to run
Firefox automated tests in the Mac OSX environment. There is a
&lt;a href=&quot;https://github.com/walac/taskcluster-worker/tree/macosx&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;macosx&lt;/code&gt; branch&lt;/a&gt; in
my personal Github taskcluster-worker fork in which I push my commits.&lt;/p&gt;

&lt;p&gt;One specific aspect of the engine implementation is the ability to run more
than one task at the same time. For this, we need to implement some kind
of task isolation. For docker-worker, each task ran in its own docker container
so tasks were isolated by definition. But there is no such thing as a container
for OSX engine. Our earlier tries with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Chroot&quot;&gt;chroot&lt;/a&gt; failed miserably, due to
incompatibilities with OSX graphic system. Our final solution was to create a new user
on the fly and run the task with this user’s credentials. This not only provides
some task isolation, but also prevents privilege escalation attacks by running
tasks with different user than the worker.&lt;/p&gt;

&lt;p&gt;Instead of dealing with the poorly documented
&lt;a href=&quot;https://developer.apple.com/library/mac/documentation/Networking/Conceptual/Open_Directory/Introduction/Introduction.html&quot;&gt;Open Directory Framework&lt;/a&gt;,
we chose to spawn the
&lt;a href=&quot;https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/dscl.1.html&quot;&gt;dscl&lt;/a&gt;
command to create and configure users. Tasks usually takes a long time to
execute, spawning loads of subprocess, so a few spawns of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dscl&lt;/code&gt; command
won’t have any practical performance impact.&lt;/p&gt;

&lt;p&gt;One final aspect is how we bootstrap task execution. A tasks boils down to
a script that executes task duties. But where does this script come from?
It doesn’t live in the machine that executes the worker. OSX engine provides a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link&lt;/code&gt; field in task payload that a task can specify an executable to download and
execute.&lt;/p&gt;

&lt;h3&gt;Running the worker&lt;/h3&gt;

&lt;p&gt;OSX engine will primarily be used to execute Firefox tests on Mac OSX,
and the environment is expected to have a very specific tools and
configurations set. Because of that, I am testing the code on a
&lt;a href=&quot;https://wiki.mozilla.org/ReleaseEngineering/How_To/Loan_a_Slave&quot;&gt;loaner machine&lt;/a&gt;.
To start the worker, it is just a matter of opening a terminal and typing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./taskcluster-worker work macosx &lt;span class=&quot;nt&quot;&gt;--logging-level&lt;/span&gt; debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The worker connects to the Taskcluster queue, claims and execute the tasks available.
At the time I am writing, all tests but &lt;em&gt;Firefox UI functional&lt;/em&gt; tests” were green,
running on optimized Firefox OSX builds. We intend to land Firefox tests in taskcluster-worker as
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Supported_build_configurations&quot;&gt;Tier-2&lt;/a&gt; on next quarter,
running them in parallel with Buildbot.&lt;/p&gt;</content:encoded>
	<dc:date>2016-06-27T00:00:00+00:00</dc:date>
</item>
<item rdf:about="tag:www.erranderr.com,2016-05-02:/blog/taskcluster-generic-tasks.html">
	<title>Maja Frydrychowicz: Not Testing a Firefox Build (Generic Tasks in TaskCluster)</title>
	<link>https://www.erranderr.com/blog/taskcluster-generic-tasks.html</link>
	<content:encoded>&lt;p&gt;A few months ago I wrote about my &lt;a href=&quot;https://www.erranderr.com/blog/taskcluster-learning.html&quot;&gt;tentative setup&lt;/a&gt; of a TaskCluster task that was neither a build nor a test. Since then, gps has implemented &lt;a href=&quot;https://groups.google.com/forum/#!searchin/mozilla.dev.platform/generic$20task/mozilla.dev.platform/bNYp2HDyeqU/tg4mnGHEAwAJ&quot;&gt;“generic” in-tree tasks&lt;/a&gt; so I &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1227367#c116&quot;&gt;adapted my initial work&lt;/a&gt; to take advantage of that.&lt;/p&gt;
&lt;h3&gt;Triggered by file changes&lt;/h3&gt;
&lt;p&gt;All along I wanted to run some in-tree tests without having them wait around for a Firefox build or any other dependencies they don’t need. So I originally implemented this task as a &lt;a href=&quot;https://www.erranderr.com/blog/taskcluster-learning.html#scheduling_summary&quot;&gt;“build”&lt;/a&gt; so that it would get scheduled for every incoming changeset in Mozilla’s repositories. &lt;/p&gt;
&lt;p&gt;But forget “builds”, forget “tests” — now there’s a third category of tasks that we’ll call “generic” and it’s exactly what I need. &lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/diff/e4ea9261d5bb/testing/taskcluster/tasks/branches/base_jobs.yml&quot;&gt;base_jobs.yml&lt;/a&gt; I say, “hey, here’s a new task called &lt;code&gt;marionette-harness&lt;/code&gt; — run it whenever there’s a change under (branch)/testing/marionette/harness”. Of course, I can also just trigger the task with try syntax like &lt;code&gt;try: -p linux64_tc -j marionette-harness -u none -t none&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When the task is triggered, a chain of events follows: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;marionette-harness&lt;/code&gt; is defined by &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/e4ea9261d5bb/testing/taskcluster/tasks/tests/harness_marionette.yml&quot;&gt;harness_marionette.yml&lt;/a&gt;, which depends on &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/e4ea9261d5bb/testing/taskcluster/tasks/harness_test.yml&quot;&gt;harness_test.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/e4ea9261d5bb/testing/taskcluster/tasks/harness_test.yml&quot;&gt;harness_test.yml&lt;/a&gt; says to run &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/diff/e4ea9261d5bb/testing/docker/desktop-build/bin/build.sh&quot;&gt;build.sh&lt;/a&gt; with the appropriate mozilla branch and revision.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/e4ea9261d5bb/testing/taskcluster/tasks/tests/harness_marionette.yml&quot;&gt;harness_marionette.yml&lt;/a&gt; sets more environment variables and parameters for build.sh to use (&lt;code&gt;JOB_SCRIPT&lt;/code&gt;, &lt;code&gt;MOZHARNESS_SCRIPT&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;So build.sh checks out the source tree and executes &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/diff/e4ea9261d5bb/testing/taskcluster/scripts/tester/harness-test-linux.sh&quot;&gt;harness-test-linux.sh&lt;/a&gt; (&lt;code&gt;JOB_SCRIPT&lt;/code&gt;)…&lt;/li&gt;
&lt;li&gt;…which in turn executes &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/1e0b4e27bd51/testing/mozharness/scripts/marionette_harness_tests.py&quot;&gt;marionette_harness_tests.py&lt;/a&gt; (&lt;code&gt;MOZHARNESS_SCRIPT&lt;/code&gt;) with the parameters passed on by build.sh&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;For Tasks that Make Sense in a gecko Source Checkout&lt;/h3&gt;
&lt;p&gt;As you can see, I made the &lt;code&gt;build.sh&lt;/code&gt; script in the &lt;code&gt;desktop-build&lt;/code&gt; docker image execute an arbitrary in-tree &lt;code&gt;JOB_SCRIPT&lt;/code&gt;, and I created &lt;code&gt;harness-test-linux.sh&lt;/code&gt; to run mozharness within a gecko source checkout. &lt;/p&gt;
&lt;h4&gt;Why not the desktop-test image?&lt;/h4&gt;
&lt;p&gt;But we can also run arbitrary mozharness scripts thanks to the configuration in the desktop-test docker image! Yes, and all of that configuration is geared toward testing a Firefox binary, which implies downloading tools that my task either doesn’t need or already has access to in the source tree. Now we have a lighter-weight option for executing tests that don’t exercise Firefox.&lt;/p&gt;
&lt;h4&gt;Why not mach?&lt;/h4&gt;
&lt;p&gt;In my lazy work-in-progress, I had originally executed the Marionette harness tests via a simple call to mach, yet now I have this crazy chain of shell scripts that leads all the way mozharness. The mach command didn’t disappear — you can run Marionette harness tests with &lt;code&gt;./mach python-test ...&lt;/code&gt;. However, mozharness provides clearer control of Python dependencies, appropriate handling of return codes to report test results to Treeherder, and I can write a job-specific script and configuration.&lt;/p&gt;</content:encoded>
	<dc:date>2016-05-02T04:00:00+00:00</dc:date>
	<dc:creator>Maja Frydrychowicz</dc:creator>
</item>
<item rdf:about="https://walac.github.io/cors-proxy/">
	<title>Wander Lairson Costa: Overcoming browser same origin policy</title>
	<link>https://walac.github.io/cors-proxy/</link>
	<content:encoded>&lt;p&gt;One of my goals for 2016 Q1 was to write a
&lt;a href=&quot;https://tools.taskcluster.net/status/&quot;&gt;monitoring dashboard&lt;/a&gt; for Taskcluster.
It basically pings Taskcluster services to check if they are alive and also
acts as a feed aggregator for services Taskcluster depends on. One problem with
this approach is the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Same-origin_policy&quot;&gt;same origin policy&lt;/a&gt;, in which
web pages are only allowed to make requests to their own domain. For web servers
which is safe to make these cross domain requests, they can either implement
&lt;a href=&quot;https://en.wikipedia.org/wiki/JSONP&quot;&gt;jsonp&lt;/a&gt; or
&lt;a href=&quot;https://en.wikipedia.org/wiki/Cross-origin_resource_sharing&quot;&gt;CORS&lt;/a&gt;. CORS is the
preferred way so we will focus on it for this post.&lt;/p&gt;

&lt;h3&gt;Cross-origin resource sharing&lt;/h3&gt;

&lt;p&gt;CORS is a mechanism that allows the web server tell the browser that is safe to
accomplish a cross domain request. It consists of a set of HTTP headers with details
for the conditions to accomplish the request. The main response header is
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access-Control-Allow-Origin&lt;/code&gt;, which contains either a list of allowed domains or
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;, indicating any domain can make a cross request to this server. In a CORS
request, only a small set of headers is exposed to the response object. The server
can tell the browser to expose additional headers through the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access-Control-Expose-Headers&lt;/code&gt; response header.&lt;/p&gt;

&lt;p&gt;But what if the web server doesn’t implement CORS? The only solution is to provide
a proxy that will make the actual request and add the CORS headers.&lt;/p&gt;

&lt;h3&gt;cors-proxy&lt;/h3&gt;

&lt;p&gt;To allow the monitoring dashboard make requests for status state on remote services
that do not implement CORS, we created the
&lt;a href=&quot;https://github.com/taskcluster/cors-proxy&quot;&gt;cors-proxy&lt;/a&gt;. It exports a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/request&lt;/code&gt;
endpoint that allows you to make requests to any remote host. cors-proxy redirects
it to the remote URL and sends the responses back, with appropriate CORS headers set.&lt;/p&gt;

&lt;p&gt;Let’s see an example:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://cors-proxy.taskcluster.net/request&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://queue.taskcluster.net/v1/ping&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The information about the remote request is sent in the proxy request body. All
parameter fields are shown in the project page.&lt;/p&gt;

&lt;p&gt;Before you think on using the hosted server to bypass your own requests, cors-proxy
only honors requests from a
&lt;a href=&quot;https://github.com/taskcluster/cors-proxy/blob/master/server.js#L12-L15&quot;&gt;whitelist&lt;/a&gt;.
So, only some subdomains under Taskcluster domain can use cors-proxy.&lt;/p&gt;</content:encoded>
	<dc:date>2016-04-01T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://petemoore.github.io/general/taskcluster/2016/03/30/windows-sshd-cygwin-ec2-aws.html">
	<title>Pete Moore: Walkthrough installing Cygwin SSH Daemon on AWS EC2 instances</title>
	<link>http://petemoore.github.io/general/taskcluster/2016/03/30/windows-sshd-cygwin-ec2-aws.html</link>
	<content:encoded>&lt;p&gt;One of the challenges we face at Mozilla is supporting Windows in an
organisational environment which is predominantly *nix oriented. Furthermore,
historically our build and test infrastructure has only provided a very limited
ssh daemon, with an antiquated shell, and outdated unix tools.&lt;/p&gt;

&lt;p&gt;With the move to hosting Windows environments in AWS EC2, the opportunity arose
to review our current SSH daemon, and see if we couldn’t do something a little
bit better.&lt;/p&gt;

&lt;p&gt;When creating Windows environments in EC2, it is possible to launch a “vanilla”
Windows instance, from an AMI created by Amazon. This instance is based on a
standard installation of a given version of Windows, with a couple of AWS EC2
tools preinstalled.&lt;/p&gt;

&lt;p&gt;One of the features of the preinstalled tools, is that they allow you to
specify powershell and/or batch script snippets inside the instance User Data,
that will be executed upon launch.&lt;/p&gt;

&lt;p&gt;This makes it quite trivial to customise a Windows environment, by providing
all of the customisation steps as a PowerShell snippet in the instance User
Data.&lt;/p&gt;

&lt;p&gt;In this Walkthrough, we will set up a Windows 2012 R2 Windows machine, with the
cygwin ssh daemon preinstalled. In order to follow this walkthrough, you will
need an AWS account, and the ability to spawn an instance.&lt;/p&gt;

&lt;h3&gt;Install AWS CLI&lt;/h3&gt;

&lt;p&gt;Although all of these steps can be performed via the web console, typically we
would want to automate them. Therefore in this walkthrough, I’m using the AWS
CLI to perform all of the actions, to make it easier should you want to script
any of the setup.&lt;/p&gt;

&lt;h5&gt;Windows installation&lt;/h5&gt;

&lt;p&gt;Download and run the &lt;a href=&quot;https://s3.amazonaws.com/aws-cli/AWSCLI64.msi&quot;&gt;64-bit&lt;/a&gt; or
&lt;a href=&quot;https://s3.amazonaws.com/aws-cli/AWSCLI32.msi&quot;&gt;32-bit&lt;/a&gt; Windows installer.&lt;/p&gt;

&lt;h5&gt;Mac and Linux installation&lt;/h5&gt;

&lt;p&gt;Requires Python 2.6.5 or higher.&lt;/p&gt;

&lt;p&gt;Install using pip.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;awscli&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h5&gt;Further help&lt;/h5&gt;

&lt;p&gt;See the AWS &lt;a href=&quot;https://aws.amazon.com/cli/&quot;&gt;CLI guide&lt;/a&gt; if you get stuck.&lt;/p&gt;

&lt;h5&gt;Configuring AWS credentials&lt;/h5&gt;

&lt;p&gt;If this is your first time running the AWS CLI tool, configure your credentials
with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;aws configure&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;See the AWS &lt;a href=&quot;http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html&quot;&gt;credentials configuration
guide&lt;/a&gt;
if you need more help.&lt;/p&gt;

&lt;h3&gt;Locate latest Windows Server 2012 R2 AMI (64bit)&lt;/h3&gt;

&lt;p&gt;The following command line will find you the latest Windows 2012 R2 stock
image, provided by AWS, in your default region.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ AMI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws ec2 describe-images &lt;span class=&quot;nt&quot;&gt;--owners&lt;/span&gt; self amazon &lt;span class=&quot;nt&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;Name=platform,Values=windows&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;Name=name,Values=Windows_Server-2012-R2_RTM-English-64Bit-Base*&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Images[*].{A:CreationDate,B:ImageId}'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
| &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we can see what the current AMI is, in our default region, with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Windows AMI: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;AMI&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
Windows AMI: ami-1719f677&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note, the actual AMI provided by AWS changes from week to week, and from region
to region, so don’t be surprised if you get a different result to the one
above.&lt;/p&gt;

&lt;h3&gt;Create a Security Group&lt;/h3&gt;

&lt;p&gt;We need our instance to be in a security group that allows us to SSH onto it.&lt;/p&gt;

&lt;p&gt;First create a security group:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ SECURITY_GROUP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws ec2 create-security-group &lt;span class=&quot;nt&quot;&gt;--group-name&lt;/span&gt; ssh-only &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--description&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SSH only&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then update it to only allow inbound SSH traffic:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SECURITY_GROUP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws ec2 authorize-security-group-ingress &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--group-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SECURITY_GROUP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--ip-permissions&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'[{&quot;IpProtocol&quot;: &quot;tcp&quot;, &quot;FromPort&quot;: 22, &quot;ToPort&quot;: 22,
&quot;IpRanges&quot;: [{&quot;CidrIp&quot;: &quot;0.0.0.0/0&quot;}]}]'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Create a unique Client Token&lt;/h3&gt;

&lt;p&gt;We should create a unique client token that will allow us to make idempotent
requests, should there be any failures. We will also use this as our “name”
for the instance until we get the real instance name back.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%s&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Create a dedicated Key Pair&lt;/h3&gt;

&lt;p&gt;We’ll need to specify a key pair in order to retrieve the Windows Password.
Let’s create a dedicated one just for this instance.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;aws ec2 create-key-pair &lt;span class=&quot;nt&quot;&gt;--key-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'KeyMaterial'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;400 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Create custom post-installation script&lt;/h3&gt;

&lt;p&gt;Typically, you’ll want to customise the cygwin environment, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Changing the bash prompt&lt;/li&gt;
  &lt;li&gt;Setting vim options&lt;/li&gt;
  &lt;li&gt;Adding ssh authorized keys&lt;/li&gt;
  &lt;li&gt;….&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s do this in a post installation bash script, which we can download as
part of the installation.&lt;/p&gt;

&lt;p&gt;In order to be able to authenticate with our new key, we’ll need to get
the public part. Note, we could generate separate keys for ssh’ing to
our machine, but we might as well reuse the key we just created.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ PUB_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ssh-keygen &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Create User Data&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-instance-metadata.html#user-data-execution&quot;&gt;AWS Windows
Guide&lt;/a&gt;
advises us that Windows PowerShell commands can be executed if supplied as part
of the EC2 User Data. We’ll use this userdata to install cygwin and the ssh
daemon from scratch.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userdata&lt;/code&gt; to store the User Data:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; userdata &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;'
&amp;lt;powershell&amp;gt;

# use TLS 1.2 (see https://bugzilla.mozilla.org/show_bug.cgi?id=1443595#c20)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# needed for making http requests
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; = New-Object system.net.WebClient

# download cygwin
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.DownloadFile(&quot;https://www.cygwin.com/setup-x86_64.exe&quot;, `
&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin-setup-x86_64.exe&quot;)

# install cygwin
# complete package list: https://cygwin.com/packages/package_list.html
Start-Process &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin-setup-x86_64.exe&quot; -ArgumentList (&quot;--quiet-mode &quot; +
&quot;--wait --root C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin --site http://cygwin.mirror.constant.com &quot; +
&quot;--packages openssh,vim,curl,tar,wget,zip,unzip,diffutils,bzr&quot;) -wait `
-NoNewWindow -PassThru -RedirectStandardOutput &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin_install.log&quot; `
-RedirectStandardError &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin_install.err&quot;

# open up firewall for ssh daemon
New-NetFirewallRule -DisplayName &quot;Allow SSH inbound&quot; -Direction Inbound `
-LocalPort 22 -Protocol TCP -Action Allow

# workaround for https://www.cygwin.com/ml/cygwin/2015-10/msg00036.html
# see:
#   1) https://www.cygwin.com/ml/cygwin/2015-10/msg00038.html
#   2) https://goo.gl/EWzeVV
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:LOGONSERVER = &quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; + &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:COMPUTERNAME

# configure sshd
Start-Process &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ash.exe&quot; -ArgumentList &quot;--login
-c `&quot;ssh-host-config -y -c 'ntsec mintty' -u 'cygwinsshd' &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
-w 'qwe123QWE!@#'`&quot;&quot; -wait -NoNewWindow -PassThru -RedirectStandardOutput `
&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygrunsrv.log&quot; -RedirectStandardError &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygrunsrv.err&quot;

# start sshd
Start-Process &quot;net&quot; -ArgumentList &quot;start cygsshd&quot; -wait -NoNewWindow -PassThru `
-RedirectStandardOutput &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;et_start_cygsshd.log&quot; `
-RedirectStandardError &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;et_start_cygsshd.err&quot;

# download bash setup script
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.DownloadFile(
&quot;https://raw.githubusercontent.com/petemoore/myscrapbook/master/setup.sh&quot;,
&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\h&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ome&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;dministrator&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;etup.sh&quot;)

# run bash setup script
Start-Process &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ash.exe&quot; -ArgumentList `
&quot;--login -c 'chmod a+x setup.sh; ./setup.sh'&quot; -wait -NoNewWindow -PassThru `
-RedirectStandardOutput &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;dministrator_cygwin_setup.log&quot; `
-RedirectStandardError &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;dministrator_cygwin_setup.err&quot;

# add SSH key
Add-Content &quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\c&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ygwin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\h&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ome&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;dministrator&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\a&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;uthorized_keys&quot; &quot;%{SSH-PUB-KEY}%&quot;
&amp;lt;/powershell&amp;gt;
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Fix SSH key&lt;/h3&gt;

&lt;p&gt;We need to replace the SSH public key placeholder we just referenced in userdata
with the actual public key&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ USERDATA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;userdata | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s_%{SSH-PUB-KEY}%_&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PUB_KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_g&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Launch new instance&lt;/h3&gt;

&lt;p&gt;We’re now finally ready to launch the instance. We can do this with the
following commands:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Please be patient, this can take a long time.&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws ec2 run-instances &lt;span class=&quot;nt&quot;&gt;--image-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;AMI&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--key-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--security-groups&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ssh-only'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--user-data&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USERDATA&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--instance-type&lt;/span&gt; c4.2xlarge &lt;span class=&quot;nt&quot;&gt;--block-device-mappings&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DeviceName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/sda1,Ebs&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'{VolumeSize=75,DeleteOnTermination=true,VolumeType=gp2}'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--instance-initiated-shutdown-behavior&lt;/span&gt; terminate &lt;span class=&quot;nt&quot;&gt;--client-token&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Instances[*].InstanceId'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PUBLIC_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class=&quot;nt&quot;&gt;--instance-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;'Reservations[*].Instances[*].NetworkInterfaces[*].Association.PublicIp'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;unset &lt;/span&gt;PASSWORD
&lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PASSWORD&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws ec2 get-password-data &lt;span class=&quot;nt&quot;&gt;--instance-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--priv-launch-key&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; PasswordData&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;10
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo
echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SSH onto your new instance (&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) with:&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;    ssh -i '&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem' Administrator@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PUBLIC_IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo
echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Note, the Administrator password is &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but it&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;should not be needed when connecting with the ssh key.&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You should get some output similar to this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Please be patient, this can take a long time.
................
SSH onto your new instance &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;i-0fe79e45ffb2c34db&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; with:
    ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'1459795270.pem'&lt;/span&gt; Administrator@54.200.218.155

Note, the Administrator password is &lt;span class=&quot;s2&quot;&gt;&quot;PItDM)Ph*U&quot;&lt;/span&gt;, but it
should not be needed when connecting with the ssh key.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded>
	<dc:date>2016-03-30T11:33:34+00:00</dc:date>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5365">
	<title>Selena Deckelmann: [workweek] tc-worker workweek recap</title>
	<link>https://www.chesnok.com/daily/2016/03/11/workweek-tc-worker-workweek-recap/</link>
	<content:encoded>&lt;h3&gt;Sprint recap&lt;/h3&gt;
&lt;p&gt;We spent this week sprinting on the tc-worker, engines and plugins. We merged 19 pull requests and had many productive discussions!&lt;/p&gt;
&lt;h4&gt;tc-worker core&lt;/h4&gt;
&lt;p&gt;We implemented the task loop! This basic loop should start when the worker is invoked. It spins up a task claimer and manager responsible for claiming as many tasks up to it’s available capacity and running them to completion. You can find details in &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/commit/6510a6c2ab9d83c88403eb473b28efcdf89b3914#diff-f64c7e5420debf6d23a76b241e871afeR16&quot;&gt;in this commit&lt;/a&gt;. We’re still working on some high level documentation.&lt;/p&gt;
&lt;p&gt;We did some cleanups to make it easier to download and get started with builds. We fixed up packages related to generating go types from json schemas, and the types now conform to the linting rules&lt;/p&gt;
&lt;p&gt;We also implemented the webhookserver. The package provides implementations of the WebHookServer interface which allows attachment and detachment of web-hooks to an internet exposed server. This will support both the livelog and interactive features. Work is detailed in &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/pull/37&quot;&gt;PR 37&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;engine: hello, world&lt;/h4&gt;
&lt;p&gt;Greg created a proof of concept and &lt;a href=&quot;https://tools.taskcluster.net/task-inspector/#a6NTsF9yQvC2cdIp1qTNrg/&quot;&gt;pushed a successful task&lt;/a&gt; to emit a hello, world artifact. Greg will be writing up something to describe this process next week.&lt;/p&gt;
&lt;h4&gt;plugin: environment variables&lt;/h4&gt;
&lt;p&gt;Wander landed this plugin this week to support environment variable setting. The work is described in &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/pull/39&quot;&gt;PR 39&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;plugin: artifact uploads&lt;/h4&gt;
&lt;p&gt;This plugin will support artifact uploads for all engines to S3 and is based on generic-worker code. This work is started in &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker/pull/55&quot;&gt;PR 55&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;TaskCluster design principles&lt;/h3&gt;
&lt;p&gt;We discussed as a team the ideas behind the design of TaskCluster. The umbrella principle we try to stick to is: Getting Things Built. We felt it was important to say that first because it helps us remember that we’re here to provide features to users, not just design systems. The four key design principles were distilled to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Self-service&lt;/li&gt;
&lt;li&gt;Robustness&lt;/li&gt;
&lt;li&gt;Enable rapid change&lt;/li&gt;
&lt;li&gt;Community friendliness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One surprising connection (to me) we made was that our privacy and security features are driven by community friendliness.&lt;/p&gt;
&lt;p&gt;We plan to add our ideas about this to a TaskCluster “about” page.&lt;/p&gt;
&lt;h3&gt;TaskCluster code review&lt;/h3&gt;
&lt;p&gt;We discussed our process for code review, and how we’d like to do them in the future. We covered issues around when to do architecture reviews and how to get “pre-reviews” for ideas done with colleagues who will be doing our reviews. We made an &lt;a href=&quot;https://public.etherpad-mozilla.org/p/taskcluster-code-review&quot;&gt;outline of ideas&lt;/a&gt; and will be giving them a permanent home on our &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;docs site&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Q2 Planning&lt;/h3&gt;
&lt;p&gt;We made a first pass at our &lt;a href=&quot;https://public.etherpad-mozilla.org/p/taskcluster-2016q2&quot;&gt;2016q2 goals&lt;/a&gt;. The main theme is to add OS X engine support to taskcluster-worker, continue work on refactoring intree config and build out our monitoring system beyond InfluxDB. Further refinements to our plan will come in a couple weeks, as we close out Q1 and get a better understanding of work related to the Buildbot to TaskCluster migration.&lt;/p&gt;</content:encoded>
	<dc:date>2016-03-11T23:48:01+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5355">
	<title>Selena Deckelmann: Tier-1 status for Linux 64 Debug build jobs on March 14, 2016</title>
	<link>https://www.chesnok.com/daily/2016/03/08/tier-1-status-for-linux-64-debug-build-jobs-on-march-14-2016/</link>
	<content:encoded>&lt;p&gt;&lt;em&gt;I sent this to dev-planning, dev-platform, sheriffs and tools-taskcluster today. I added a little more context for a non-Mozilla audience.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The time has come! We are planning to switch to &lt;a href=&quot;https://wiki.mozilla.org/Sheriffing/Job_Visibility_Policy&quot;&gt;Tier-1 on Treeherder&lt;/a&gt; for TaskCluster &lt;a href=&quot;https://treeherder.mozilla.org/#/jobs?repo=mozilla-central&amp;amp;filter-searchStr=linux%20x64%20debug&quot;&gt;Linux 64 Debug&lt;/a&gt; build jobs on March 14. At the same time, we will hide the Buildbot build jobs, but continue running them. This means that these jobs will become what Sheriffs use to determine the health of patches and our trees.&lt;/p&gt;
&lt;p&gt;On March 21, we plan to switch the Linux 64 Debug tests to Tier-1 and hide the related Buildbot test jobs.&lt;/p&gt;
&lt;p&gt;After about 30 days, we plan to disable and remove all Buildbot jobs related to Linux Debug.&lt;/p&gt;
&lt;p&gt;Background:&lt;/p&gt;
&lt;p&gt;We’ve been running Linux 64 Debug builds and tests using TaskCluster side-by-side with Buildbot jobs since February 18th. Some of the project work that was done to green up the tests is documented &lt;a href=&quot;https://public.etherpad-mozilla.org/p/buildbot-to-taskcluster-migration&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new tests are running in Docker-ized environments, and the Docker images we use are defined in-tree and publicly accessible.&lt;/p&gt;
&lt;p&gt;This work was the culmination of many months of effort, with Joel Maher, Dustin Mitchell and Armen Zambrano primarily focused on test migration this quarter. Thank you to everyone who responded to NEEDINFOs, emails and pings on IRC to help with untangling busted test runs.&lt;/p&gt;
&lt;p&gt;On performance, we’re taking a 14% hit across all the new test jobs vs. the old jobs in Buildbot. We ran two large-scale tests to help determine where slowness might still be lurking, and were able to find and fix many issues. There are a handful of jobs remaining that seem significantly slower, while others are significantly faster. We decided that it was more important to deprecate the old jobs and start exclusively maintaining the new jobs now, rather than wait to resolve the remaining performance issues. Over time we hope to address issues with the owners of the affected test suites.&lt;/p&gt;</content:encoded>
	<dc:date>2016-03-08T22:20:58+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5357">
	<title>Selena Deckelmann: [portland] taskcluster-worker Hello, World</title>
	<link>https://www.chesnok.com/daily/2016/03/07/portland-taskcluster-worker-hello-world/</link>
	<content:encoded>&lt;p&gt;The &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;TaskCluster Platform&lt;/a&gt; team is in Portland this week, hacking on the &lt;a href=&quot;https://github.com/taskcluster/taskcluster-worker&quot;&gt;taskcluster-worker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today, we all sync’d up on the current state of our worker, and what we’re going to hack on this week. We started with &lt;a href=&quot;https://godoc.org/github.com/taskcluster/taskcluster-worker&quot;&gt;the current docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The reason why we’re investing so much time in the worker is two fold:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The worker code previously lived in two code bases – docker-worker and generic-worker. We need to unify these code bases so that multiple engineers can work on it, and to help us maintain feature parity.&lt;/li&gt;
&lt;li&gt;We need to get a worker that supports Windows into production. For now, we’re using the generic-worker, but we’d like to switch over to taskcluster-worker in late Q2 or early Q3. This timeline lines up with when we expect the Windows migration from Buildbot to happen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One of the things I asked this team to do was come up with some demos of the new worker. The first demo today was to &lt;a href=&quot;https://tools.taskcluster.net/task-inspector/#a6NTsF9yQvC2cdIp1qTNrg/&quot;&gt;simply output a log and upload it&lt;/a&gt; from Greg Arndt.&lt;/p&gt;
&lt;p&gt;The rest of the team is getting their Go environments set up to run tests and get hacking on crucial plugins, like our environment variable handling and additional artifact uploading logic we need for our production workers.&lt;/p&gt;
&lt;p&gt;We’re also taking the opportunity to sync up with our Windows environment guru. Our goal for Buildbot to TaskCluster migration this quarter is focused on Linux builds and tests. Next quarter, we’ll be finishing Linux and, I hope, landing Windows builds in TaskCluster. To do that, we have a lot of details to sort out with how we’ll build Windows AMIs and deploy them. It’s a very different model because we don’t have the same options with Docker as we have on Linux.&lt;/p&gt;</content:encoded>
	<dc:date>2016-03-07T23:51:50+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="https://jonasfj.dk/?p=481">
	<title>Jonas Finnemann Jensen: One-Click Loaners with TaskCluster</title>
	<link>https://jonasfj.dk/2016/03/one-click-loaners-with-taskcluster/</link>
	<content:encoded>&lt;p&gt;Last summer &lt;a href=&quot;https://air.mozilla.org/edgar-chen-taskcluster-interactive-sessions/&quot;&gt;Edgar Chen (air.mozilla.org)&lt;/a&gt; built on an interactive shell for TaskCluster Linux workers, so developers can get a SSH-like session into a task container from their browser. We’ve slowly been improving this, and prior to Mozlando I added support for opening a VNC-like session connecting to an X-session inside a task container. I’ll admit I was mostly motivated by the prospect of giving an impressive demo, and the implementation details are likely to change as we improve it further. Consequently, we haven’t got many guides on how to use these features in their current state.&lt;/p&gt;
&lt;p&gt;However, with people asking for TaskCluster “loaners” on IRC, I figure now is a good time to explain how these interactive features can be used to provide a loaner-on-demand flow for TaskCluster workers. At least on Linux, but hopefully we can do a similar thing on other platforms too. Before we dive in, I want to note that all of our Linux tasks runs under &lt;a href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; with one container per tasks. Hence, you can pull down the docker image and play with it locally, the process and caveats such as setting up loopback video and audio devices is beyond the scope of this post. But feel free to ask on IRC (#taskcluster), I’m sure &lt;a href=&quot;http://blog.gregarndt.com/&quot;&gt;Greg Arndt&lt;/a&gt; has all the details, some of them are already present in “Run Locally” script displayed in the task-inspector.&lt;/p&gt;
&lt;h3&gt;Quick Start&lt;/h3&gt;
&lt;p&gt;If you can’t wait to play, here are the bullet points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You’ll need a commit-level 1 access (and LDAP login)&lt;/li&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://treeherder.mozilla.org&quot;&gt;treeherder.mozilla.org&lt;/a&gt; pick &lt;a href=&quot;https://treeherder.mozilla.org/#/jobs?repo=mozilla-central&amp;amp;filter-searchStr=tc&quot;&gt;a task that runs on TaskCluster&lt;/a&gt; (I tried “[TC] Linux64 reftest-3”, build tasks don’t have X.org)&lt;/li&gt;
&lt;li&gt;Under “Job details” click the “Inspect Task” (this will open the task-inspector)&lt;/li&gt;
&lt;li&gt;In the top right corner in the task-inspector click “Login” (this opens login.taskcluster.net on a new tab)
&lt;ol&gt;
&lt;li&gt;“Sign-in with LDAP” or  “Sign-in with Okta” (Okta only works for employees)&lt;/li&gt;
&lt;li&gt;Click the “Grant Access” button (to grant tools.taskcluster.net access)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;In the task-inspector under the “Task” tab, scroll down and click the “One-Click Loaner” button&lt;/li&gt;
&lt;li&gt;Click again to confirm and create a one-click loaner task (this takes you to a “Waiting for Loaner” page)
&lt;ol&gt;
&lt;li&gt;Just wait… 30s to 5 min (you can open the task-inspector for your loaner task to see the live log, if you are impatient)&lt;/li&gt;
&lt;li&gt;Eventually you should see two big buttons to open an interactive shell or display&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;You should now have an interactive terminal (and display) into a running task container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Warning: These loaners runs on EC2 spot-nodes, they may disappear at any time. Use them for quickly trying something, not for writing patches.&lt;/p&gt;
&lt;p&gt;Given all these steps, in particular the “Click again” in step (6), I recognize that it might take more than one click to get a “One-Click Loaner”. But we are just getting started, and all of this should be considered a moving target. The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Running_automated_tests/TaskCluster_interactive_session&quot;&gt;instructions above can also be found on MDN&lt;/a&gt;, where we will try to keep them up to date.&lt;/p&gt;
&lt;h3&gt;Implementation Details&lt;/h3&gt;
&lt;p&gt;To support interactive shell sessions the worker has an end-point that accepts websocket connections. For each new websocket the worker spawns a &lt;code&gt;sh&lt;/code&gt; or &lt;code&gt;bash&lt;/code&gt; inside the task container and pipes &lt;code&gt;stdin&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;stderr&lt;/code&gt; over the websocket. In browser we use then have the websocket reading from and writing to &lt;a href=&quot;https://github.com/chromium/hterm&quot;&gt;hterm (from the chromium project)&lt;/a&gt; giving us a nice terminal emulator in the browser. There is still a few issues with the &lt;a href=&quot;https://github.com/docker/docker/issues/8755&quot;&gt;TTY emulation in docker&lt;/a&gt;, but it works reasonably for small things.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;shell&quot; class=&quot;aligncenter size-full wp-image-482&quot; height=&quot;584&quot; src=&quot;https://jonasfj.dk/blog/wp-content/uploads/2015/12/shell.png&quot; width=&quot;789&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For interactive display sessions (VNC-like sessions in the browser) the worker has an end-point which accepts both websocket connections and ordinary &lt;code&gt;GET&lt;/code&gt; requests for listing displays. For each &lt;code&gt;GET&lt;/code&gt; request the worker will run a small statically linked binary that lists all the X-sessions inside the task container, the result is then transformed to JSON and returned in the request. Once the user has picked a display, a websocket connection is opened with the display identifier in query-string. On the worker the websocket is piped to a statically linked instance of &lt;a href=&quot;http://www.karlrunge.com/x11vnc/&quot;&gt;x11vnc&lt;/a&gt; running inside the task container. In the browser we then use &lt;a href=&quot;https://github.com/kanaka/noVNC&quot;&gt;noVNC&lt;/a&gt; to give the user an interactive remote display right in the browser.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;novnc&quot; class=&quot;aligncenter size-full wp-image-483&quot; height=&quot;584&quot; src=&quot;https://jonasfj.dk/blog/wp-content/uploads/2015/12/novnc.png&quot; width=&quot;789&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As with the shell, there is also a few quirks to the interactive display. Some graphical artifacts and other “interesting” issues. When streaming a TCP connection over a websocket we might not be handling buffering all too well. Which I suspect introduces additional latency and possible bugs. I hope these things will get better in future iterations of the worker, which is currently undergoing an experimental rewrite from node to go.&lt;/p&gt;
&lt;h3&gt;Future Work&lt;/h3&gt;
&lt;p&gt;As mentioned in the “Quick Start” section, all of this is still a bit of a moving target. Access is to any loaner is effectively granted to anyone with commit level 1 or any employee. So your friends can technically hijack the interactive task you created. Obviously, we have to make that more fine-grained. At the moment, the “one-click loaner” button is also very specific to our Linux worker. As we add more platforms will have to extend support and find a way to abstract the platform dependent aspects. S it’s very likely that this will break on occasion.&lt;/p&gt;
&lt;p&gt;We also recently introduced a hack defining the environment variable &lt;code&gt;TASKCLUSTER_INTERACTIVE&lt;/code&gt; when a loaner task is created. A quick hack that we might refactor later, but for now it’s enabling &lt;a href=&quot;http://armenzg.blogspot.com/&quot;&gt;Armen Zambrano&lt;/a&gt; to customize how the docker image used for tests runs in loaner-mode. In &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1250904&quot;&gt;bug 1250904&lt;/a&gt; there is on-going work to ensure that a loaner will setup the test environment, but not start running tests until a user connects and types the right command. I’m sure there are many other things we can do to make the task environment more useful in loaner-mode, but this is certainly a good start.&lt;/p&gt;
&lt;p&gt;Anyways, much of this is still quick hacks, with rough edges that needs to be resolved. So don’t be surprised if it breaks while we improve stability and attempt to add support for multiple platforms. With a bit of time and resources I’m fairly confident that the “one-click loaner” flow could become the preferred method for debugging issues specific to the test environment.&lt;/p&gt;</content:encoded>
	<dc:date>2016-03-01T06:02:34+00:00</dc:date>
	<dc:creator>jonasfj</dc:creator>
</item>
<item rdf:about="tag:blogger.com,1999:blog-2324292491075571582.post-4384759603685314095">
	<title>John Ford: cloud-mirror – Platform Engineering Operations Project of the Month</title>
	<link>http://blog.johnford.org/2016/02/cloud-mirror-platform-engineering.html</link>
	<content:encoded>Hello from Platform Engineering Operations!  Once a month we highlight one of our projects to help
the Mozilla community discover a useful tool or an interesting contribution
opportunity.  This month's project is our cloud-mirror.&lt;br /&gt;
&lt;br /&gt;
The cloud-mirror is something that we've written to reduce costs and time of
inter-region S3 transfers.  Cloud-mirror was designed for use in the
Taskcluster system, but is possible to run independently.  Taskcluster, which
is the new automation environment for Mozilla, can support passing artifacts
between dependent tasks.  An example of this is that when we do a build, we
want to make the binaries available to the test machines.  We originally hosted
all of our artifacts in a single AWS region.  This meant that every time a test
was done in a region outside of the main region, we would incur an inter-region
transfer for each test run.  This is expensive and slow compared to in-region
transfers.&lt;br /&gt;
&lt;br /&gt;
We decided that a better idea would be to transfer the data from the main
region to the other regions the first time it was requested in that region and
then have all subsequent requests be inside of the region.  This means that for
the small overhead of an extra in-region copy of the file, we lose the cost and
time overhead of doing inter-region transfers every single time.&lt;br /&gt;
&lt;br /&gt;
Here's an example.  We use us-west-2 as our main region for storing artifacts.
A test machine in eu-central-1 requires &quot;firefox-50.tar.bz2&quot; for use in a test.
The test machine in eu-central-1 will ask cloud mirror for this file.  Since
this is the first test to request this artifact in eu-central-1, cloud mirror
will first copy &quot;firefox-50.tar.bz2&quot; into eu-central-1 then redirect to the
copy of that file in eu-central-1.  The second test machine in eu-central-1
will then ask for a copy of &quot;firefox-50.tar.bz2&quot; and because it's already in
the region, the cloud mirror will immediately redirect to the eu-central-1
copy.&lt;br /&gt;
&lt;br /&gt;
We expire artifacts from the destination regions so that we don't incur too
high storage costs.  We also use a redis cache configured to expire keys which
have been used least recently first.  Cloud mirror is written with Node 5 and
uses Redis for storage.  We use the upstream aws-sdk library for doing our S3
operations.&lt;br /&gt;
&lt;br /&gt;
We're in the process of deploying this system to replace our original
implementation called 's3-copy-proxy'.  This earlier version was a much simpler
version of this idea which we've been using in production.  One of the main
reasons for the rewrite was to be able to abstract the core concepts to allow
anyone to write a backend for their storage type as well as being able to
support more aws regions and move towards a completely HTTPS based chain.&lt;br /&gt;
&lt;br /&gt;
If this is a project that's interesting to you, we have lots of ways that you
could contribute!  Here are some:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;switch polling for pending copy operations to use redis's pub/sub features&lt;/li&gt;
&lt;li&gt;write an Azure or GCE storage backend&lt;/li&gt;
&lt;li&gt;Modify the API to determine which cloud storage pool a request should be redirected to instead of having to encode that into the route&lt;/li&gt;
&lt;li&gt;Write a localhost storage backend for testing that serves content on 127.0.0.1&lt;/li&gt;
&lt;/ul&gt;
If you have any ideas or find some bugs in this system, please open an issue &lt;a href=&quot;https://github.com/taskcluster/cloud-mirror/issues&quot;&gt;https://github.com/taskcluster/cloud-mirror/issues&lt;/a&gt;.  For the time
being, you will need to have an AWS account to run our integration tests (`npm
test`).  We would love to have a storage backend that allows running the
non-service specific portions of the system without any extra permissions.&lt;br /&gt;
If you're interested in contributing, please ping me (jhford) in #taskcluster
on irc.mozilla.org.&lt;br /&gt;
&lt;br /&gt;
For more information about all Platform Ops projects, visit our wiki.  If
you're interested in helping out, &lt;a href=&quot;http://ateam-bootcamp.readthedocs.org/en/latest/guide/index.html&quot;&gt;http://ateam-bootcamp.readthedocs.org/en/latest/guide/index.html&lt;/a&gt; has resources for getting
started.</content:encoded>
	<dc:date>2016-02-24T15:13:36+00:00</dc:date>
	<dc:creator>Unknown</dc:creator>
</item>
<item rdf:about="tag:www.erranderr.com,2016-02-09:/blog/taskcluster-learning.html">
	<title>Maja Frydrychowicz: First Experiment with TaskCluster</title>
	<link>https://www.erranderr.com/blog/taskcluster-learning.html</link>
	<content:encoded>&lt;p&gt;&lt;a href=&quot;https://docs.taskcluster.net/&quot;&gt;TaskCluster&lt;/a&gt; is a new-ish continuous integration system made at Mozilla. It manages the scheduling and execution of tasks based on a graph of their dependencies. It’s a general &lt;span class=&quot;caps&quot;&gt;CI&lt;/span&gt; tool, and could be used for any kind of job, not just Mozilla things. &lt;/p&gt;
&lt;p&gt;However, the example I describe here refers to a Mozilla-centric use case of TaskCluster&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;: tasks are run per check-in on the branches of Mozilla’s Mercurial repository and then results are posted to &lt;a href=&quot;https://github.com/mozilla/treeherder&quot;&gt;Treeherder&lt;/a&gt;. For now, the tasks can be configured to run in Docker images (Linux), but other platforms are in the works&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. &lt;/p&gt;
&lt;p&gt;So, I want to schedule a task! I need to add a new task to the task graph that’s created for each revision submitted to hg.mozilla.org. (This is part of my work on deploying a suite of &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1227367&quot;&gt;tests for the Marionette Python test runner&lt;/a&gt;, i.e. testing the test harness itself.) &lt;/p&gt;
&lt;p&gt;The rest of this post describes what I learned while making &lt;a href=&quot;https://hg.mozilla.org/try/rev/6b7479c4aa30&quot;&gt;this work-in-progress&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;There are builds and there are tests&lt;/h3&gt;
&lt;p&gt;mozilla-taskcluster operates based on the info under &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks&quot;&gt;&lt;code&gt;testing/taskcluster/tasks&lt;/code&gt;&lt;/a&gt; in Mozilla’s source tree, where there are yaml files that describe tasks. Specific tasks can inherit common configuration options from base yaml files. &lt;/p&gt;
&lt;p&gt;The yaml files are organized into two main categories of tasks: builds and tests. This is just a convention in mozilla-taskcluster about how to group task configurations; &lt;span class=&quot;caps&quot;&gt;TC&lt;/span&gt; itself doesn’t actually know or care whether a task is a build or a test.&lt;/p&gt;
&lt;p&gt;The task I’m creating doesn’t quite fit into either category: it runs harness tests that just exercise the Python runner code in &lt;a href=&quot;https://marionette-client.readthedocs.org&quot;&gt;marionette_client&lt;/a&gt;, so I only need a source checkout, not a Firefox build. I’d like these tests to run quickly without having to wait around for a build. Another example of such a task is the recently-created &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/rev/4b34c9d1a31a&quot;&gt;ESLint task&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Scheduling a task&lt;/h3&gt;
&lt;p&gt;Just adding a yaml file that describes your new task under &lt;code&gt;testing/taskcluster/tasks&lt;/code&gt; isn’t enough to get it scheduled: you must also add it to the list of tasks in &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks/branches/base_jobs.yml&quot;&gt;&lt;code&gt;base_jobs.yml&lt;/code&gt;&lt;/a&gt;, and define an identifier for your task in &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks/branches/base_job_flags.yml&quot;&gt;&lt;code&gt;base_job_flags.yml&lt;/code&gt;&lt;/a&gt;. This identifier is used in &lt;code&gt;base_jobs.yml&lt;/code&gt;, and also by people who want to run your task when pushing to &lt;a href=&quot;https://wiki.mozilla.org/ReleaseEngineering/TryServer&quot;&gt;try&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How does scheduling work? First a &lt;a href=&quot;https://docs.taskcluster.net/introduction/getting-started/#decision-tasks-and-task-graphs&quot;&gt;decision task&lt;/a&gt; generates a &lt;em&gt;task graph&lt;/em&gt;, which describes all the tasks and their relationships. More precisely, it looks at &lt;code&gt;base_jobs.yml&lt;/code&gt; and other yaml files in &lt;code&gt;testing/taskcluster/tasks&lt;/code&gt; and spits out a json artifact, &lt;code&gt;graph.json&lt;/code&gt;&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-3&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Then, &lt;code&gt;graph.json&lt;/code&gt; gets sent to &lt;span class=&quot;caps&quot;&gt;TC&lt;/span&gt;’s &lt;a href=&quot;https://docs.taskcluster.net/queue/api-docs/#createTask&quot;&gt;&lt;code&gt;createTask&lt;/code&gt;&lt;/a&gt; endpoint, which takes care of the actual scheduling.  &lt;/p&gt;
&lt;p&gt;In the excerpt below, you can see a task definition with a &lt;code&gt;requires&lt;/code&gt; field and you can recognize a lot of fields that are in common with the ‘task’ section of the yaml files under &lt;code&gt;testing/taskcluster/tasks/&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;tasks&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;requires&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// id of a build task that this task depends on&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;fZ42HVdDQ-KFFycr9PxptA&quot;&lt;/span&gt;  
      &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;task&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;taskId&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;c2VD_eCgQyeUDVOjsmQZSg&quot;&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;extra&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;treeherder&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;s2&quot;&gt;&quot;groupName&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Reftest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
              &lt;span class=&quot;s2&quot;&gt;&quot;groupSymbol&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tc-R&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Reftest test run 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[TC] Reftest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;c1&quot;&gt;//...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;For now at least, a major assumption in the task-graph creation process seems to be that &lt;em&gt;test&lt;/em&gt; tasks can depend on &lt;em&gt;build&lt;/em&gt; tasks and &lt;em&gt;build&lt;/em&gt; tasks don’t really&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-4&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; depend on anything. So: &lt;a name=&quot;scheduling_summary&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want your tasks to run for every push to a Mozilla hg branch, add it to the list of &lt;strong&gt;builds&lt;/strong&gt; in &lt;code&gt;base_jobs.yml&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;If you want your task to run after certain build tasks succeed, add it to the list of &lt;strong&gt;tests&lt;/strong&gt; in &lt;code&gt;base_jobs.yml&lt;/code&gt; and specify which build tasks it depends on.&lt;/li&gt;
&lt;li&gt;Other than the above, I don’t see any way to specify a dependency between task A and task B in &lt;code&gt;testing/taskcluster/tasks&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, I added &lt;code&gt;marionette-harness&lt;/code&gt; under &lt;code&gt;builds&lt;/code&gt;. Recall, my task isn’t a build task, but it doesn’t depend on a build, so it’s not a test, so I’ll treat it like a build.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# in base_job_flags.yml&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;builds&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;marionette-harness&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# in base_jobs.yml&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;builds&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;marionette-harness&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;platforms&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;Linux64&lt;/span&gt;
    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;tasks/tests/harness_marionette.yml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will allow me to trigger my task with the following try syntax: &lt;code&gt;try: -b o -p marionette-harness&lt;/code&gt;. Cool.&lt;/p&gt;
&lt;h3&gt;Make your task do stuff&lt;/h3&gt;
&lt;p&gt;Now I have to add some stuff to &lt;code&gt;tasks/tests/harness_marionette.yml&lt;/code&gt;. Many of my choices here are based on the work done for the &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/rev/4b34c9d1a31a&quot;&gt;ESLint task&lt;/a&gt;. I created a base task called &lt;code&gt;harness_test.yml&lt;/code&gt; by mostly copying bits and pieces from the basic build task, &lt;code&gt;build.yml&lt;/code&gt; and making a few small changes. The actual task, &lt;code&gt;harness_marionette.yml&lt;/code&gt; inherits from &lt;code&gt;harness_test.yml&lt;/code&gt; and defines specifics like Treeherder symbols and the command to run.&lt;/p&gt;
&lt;h4&gt;The command&lt;/h4&gt;
&lt;p&gt;The heart of the task is in &lt;code&gt;task.payload.command&lt;/code&gt;. You could chain a bunch of shell commands together directly in this field of the yaml file, but it’s better not to. Instead, it’s common to call a TaskCluster-friendly shell script that’s available in your task’s environment. For example, the &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker/desktop-test&quot;&gt;&lt;code&gt;desktop-test&lt;/code&gt;&lt;/a&gt; docker image has a script called &lt;code&gt;test.sh&lt;/code&gt; through which you can call the &lt;a href=&quot;https://wiki.mozilla.org/ReleaseEngineering/Mozharness&quot;&gt;mozharness&lt;/a&gt; script for your tests. There’s a similar &lt;code&gt;build.sh&lt;/code&gt; script on &lt;code&gt;desktop-build&lt;/code&gt;. Both of these scripts depend on environment variables set elsewhere in your task definition, or in the Docker image used by your task. The environment might also provide utilities like &lt;a href=&quot;https://tc-vcs.readthedocs.org/en/latest/&quot;&gt;tc-vcs&lt;/a&gt;, which is used for checking out source code.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# in harness_marionette.yml&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;+ bash&lt;/span&gt;
    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;+ -cx&lt;/span&gt;
    &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;+ &amp;gt;&lt;/span&gt;
        &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;tc-vcs checkout ./gecko {{base_repository}} {{head_repository}} {{head_rev}} {{head_ref}} &amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;cd gecko &amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;./mach marionette-harness-test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;My task’s &lt;code&gt;payload.command&lt;/code&gt; should be moved into a custom shell script, but for now it just chains together the source checkout and a call to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/mach&quot;&gt;mach&lt;/a&gt;. It’s not terrible of me to use mach in this case because I expect my task to work in a build environment, but most tests would likely call mozharness.&lt;/p&gt;
&lt;h3&gt;Configuring the task’s environment&lt;/h3&gt;
&lt;p&gt;Where should the task run? What resources should it have access to? This was probably the hardest piece for me to figure out.&lt;/p&gt;
&lt;h4&gt;docker-worker&lt;/h4&gt;
&lt;p&gt;My task will run in a docker image using a &lt;a href=&quot;https://docs.taskcluster.net/workers/docker-worker/&quot;&gt;docker-worker&lt;/a&gt;&lt;sup id=&quot;fnref-5&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-5&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. The image, called &lt;code&gt;desktop-build&lt;/code&gt;, is defined in-tree under &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker/desktop-build&quot;&gt;&lt;code&gt;testing/docker&lt;/code&gt;&lt;/a&gt;. There are many other images defined there, but I only considered &lt;code&gt;desktop-build&lt;/code&gt; versus &lt;code&gt;desktop-test&lt;/code&gt;. I opted for &lt;code&gt;desktop-build&lt;/code&gt; because &lt;code&gt;desktop-test&lt;/code&gt; seems to contain mozharness-related stuff that I don’t need for now.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# harness_test.yml&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'task-image'&lt;/span&gt;
   &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'public/image.tar'&lt;/span&gt;
   &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;taskId&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'{{#task_id_for_image}}desktop-build{{/task_id_for_image}}'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The image is stored as an artifact of another &lt;span class=&quot;caps&quot;&gt;TC&lt;/span&gt; task, which makes it a ‘task-image’. Which artifact? The default is &lt;code&gt;public/image.tar&lt;/code&gt;. Which task do I find the image in? The magic incantation &lt;code&gt;'{{#task_id_for_image}}desktop-build{{/task_id_for_image}}'&lt;/code&gt; somehow&lt;sup id=&quot;fnref-6&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fn-6&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; obtains the correct &lt;span class=&quot;caps&quot;&gt;ID&lt;/span&gt;, and if I look at a particular run of my task, the above snippet does indeed get populated with an actual &lt;code&gt;taskId&lt;/code&gt;. &lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;public/image.tar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Mystery task that makes a desktop-build image for us. Thanks, mystery task!&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;taskId&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aqt_YdmkTvugYB5b-OvvJw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;task-image&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Snooping around in the handy &lt;a href=&quot;https://tools.taskcluster.net/task-inspector/&quot;&gt;Task Inspector&lt;/a&gt;, I found that the magical mystery task is defined in &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks/image.yml&quot;&gt;image.yml&lt;/a&gt; and runs &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker/image_builder/bin/build_image.sh&quot;&gt;&lt;code&gt;build_image.sh&lt;/code&gt;&lt;/a&gt;. Fun. It’s also quite convenient to &lt;a href=&quot;https://docs.taskcluster.net/presentations/TC-102/#/images-00&quot;&gt;define and test your own custom image&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Other details that I mostly ignored&lt;/h4&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# in harness_test.yml&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Nearly all of our build tasks use tc-vcs&lt;/span&gt;
  &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'docker-worker:cache:level-{{level}}-{{project}}-tc-vcs'&lt;/span&gt;
&lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;# The taskcluster-vcs tooling stores the large clone caches in this&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;# directory and will reuse them for new requests this saves about 20s~&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;# and is the most generic cache possible.&lt;/span&gt;
   &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;level-{{level}}-{{project}}-tc-vcs&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'/home/worker/.tc-vcs'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Routes&lt;/em&gt; allow your task to be looked up in the task index. This isn’t necessary in my case so I just omitted routes altogether.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Scopes&lt;/em&gt; are permissions for your tasks, and I just copied the scope that is used for checking out source code.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;workerType&lt;/em&gt; is a configuration for managing the workers that run tasks. To me, this was a choice between &lt;code&gt;b2gtest&lt;/code&gt; and &lt;code&gt;b2gbuild&lt;/code&gt;, which aren’t specific to b2g anyway. &lt;code&gt;b2gtest&lt;/code&gt; is more lightweight, I hear, which suits my harness-test task fine.&lt;/li&gt;
&lt;li&gt;I had to include a few dummy values under &lt;code&gt;extra&lt;/code&gt; in &lt;code&gt;harness_test.yml&lt;/code&gt;, like &lt;code&gt;build_name&lt;/code&gt;, just because they are expected in &lt;em&gt;build&lt;/em&gt; tasks. I don’t use these values for anything, but my task fails to run if I don’t include them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Yay for trial and error&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;If you have syntax errors in your yaml, the Decision task will fail. If this happens during a try push, look under Job Details &amp;gt; Inspect Task to fine useful error messages.&lt;/li&gt;
&lt;li&gt;Iterating on your task is pretty easy. Aside from pushing to try, you can &lt;a href=&quot;https://docs.taskcluster.net/presentations/TC-101/#/run-locally-environment&quot;&gt;run tasks locally using vagrant&lt;/a&gt; and you can build a task graph locally as well with &lt;code&gt;mach taskcluster-graph&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;p&gt;Blog posts from other TaskCluster users at Mozilla:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ehsanakhgari.org/blog/2015-09-29/my-experience-adding-new-build-type-taskcluster&quot;&gt;https://ehsanakhgari.org/blog/2015-09-29/my-experience-adding-new-build-type-taskcluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elvis314.wordpress.com/2015/11/09/adventures-in-task-cluster-running-tests-locally/&quot;&gt;https://elvis314.wordpress.com/2015/11/09/adventures-in-task-cluster-running-tests-locally/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elvis314.wordpress.com/2015/11/11/adventures-in-task-cluster-running-a-custom-docker-image/&quot;&gt;https://elvis314.wordpress.com/2015/11/11/adventures-in-task-cluster-running-a-custom-docker-image/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is lots of great documentation at &lt;a href=&quot;https://docs.taskcluster.net&quot;&gt;docs.taskcluster.net&lt;/a&gt;, but these sections were especially useful to me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.taskcluster.net/queue/api-docs/#createTask&quot;&gt;createTask &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.taskcluster.net/workers/&quot;&gt;Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;http://code.v.igoro.us/&quot;&gt;dustin&lt;/a&gt;, pmoore and others for corrections and feedback.&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;This is accomplished in part thanks to &lt;a href=&quot;http://blog.gregarndt.com/taskcluster/2015/08/05/demystifying-in-tree-scheduling/&quot;&gt;mozilla-taskcluster&lt;/a&gt;, a service that links Mozilla’s hg repo to TaskCluster and creates each decision task. More at &lt;a href=&quot;https://docs.taskcluster.net/introduction/getting-started/#taskcluster-at-mozilla&quot;&gt;TaskCluster at Mozilla&lt;/a&gt; &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-1&quot; title=&quot;Jump back to footnote 1 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;Run tasks on any platform thanks to &lt;a href=&quot;https://docs.taskcluster.net/workers/generic-worker/&quot;&gt;generic worker&lt;/a&gt; &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-2&quot; title=&quot;Jump back to footnote 2 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;To look at a &lt;code&gt;graph.json&lt;/code&gt; artifact, go to &lt;a href=&quot;https://treeherder.mozilla.org/&quot;&gt;Treeherder&lt;/a&gt;, click a green ‘D’ job, then Job details &amp;gt; Inspect Task, where you should find a list of artifacts. &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-3&quot; title=&quot;Jump back to footnote 3 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;It’s not &lt;em&gt;really&lt;/em&gt; true that build tasks don’t depend on anything. Any task that uses a task-image depends on the task that creates the image. I’m sorry for saying ‘task’ five times in every sentence, by the way. &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-4&quot; title=&quot;Jump back to footnote 4 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;…as opposed to a &lt;a href=&quot;https://docs.taskcluster.net/workers/generic-worker/&quot;&gt;generic worker&lt;/a&gt;. &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-5&quot; title=&quot;Jump back to footnote 5 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-6&quot;&gt;
&lt;p&gt;&lt;code&gt;{{#task_id_for_image}}&lt;/code&gt; is an example of a predefined variable that we can use in our &lt;span class=&quot;caps&quot;&gt;TC&lt;/span&gt; yaml files. Where do they come from? How do they get populated? I don’t know. &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.erranderr.com/blog/feeds/taskcluster.atom.xml#fnref-6&quot; title=&quot;Jump back to footnote 6 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded>
	<dc:date>2016-02-16T05:00:00+00:00</dc:date>
	<dc:creator>Maja Frydrychowicz</dc:creator>
</item>
<item rdf:about="tag:blogger.com,1999:blog-2324292491075571582.post-1771023270396663807">
	<title>John Ford: Splitting out taskcluster-base into component libraries</title>
	<link>http://blog.johnford.org/2015/10/splitting-out-taskcluster-base-into.html</link>
	<content:encoded>The intended audience of this post is people who either work with taskcluster-base now or are interested in implementing taskcluster services in the future.&lt;br /&gt;
&lt;br /&gt;
Taskcluster serverside components are currently built using the suite of libraries in the taskcluster-base npm package.  This package is many things: config parsing, data persistence, statistics, json schema validators, pulse publishers, a rest api framework and some other useful tools.  Having these all in one single package means that each time a contributor wants to hack on one part of our platform, she'll have to figure out how to install and run all of our dependencies.  This is annoying when it's waiting for a libxml.so library build, but just about impossible for contributors who aren't on the Taskcluster platform team.  You need ​Azure, Influx and AWS accounts to be able to run the full test suite.  You also might experience confusing errors in a part of the library you're not even touching.&lt;br /&gt;
&lt;br /&gt;
Additionally, we are starting to get to the point where some services must upgrade one part of taskcluster-base without using other parts.  This is generally frowned upon, but sometimes we just need to put a bandaid on a broken system that's being turned off soon.  We deal with this currently by exporting base.Entity and base.LegacyEntity.  I'd much rather we just export a single base.Entity and have people who need to keep using the old Entity library use taskcluster-lib-legacyentity directly&lt;br /&gt;
&lt;br /&gt;
We're working on fixing this!  The structure of taskcluster-base is really primed and ready to be split up since it's already a bunch of independent libraries that just so happen to be collocated.  The new component loader that landed was the first library to be included in taskcluster-base this way and I converted our configs and stats libraries last week.&lt;br /&gt;
&lt;br /&gt;
The naming convention that we've settled on is that taskcluster libraries will be prefix with taskcluster-lib-X.  This means we have taskcluster-lib-config, taskcluster-lib-stats.  We'll continue to name services as taskcluster-Y, like taskcluster-auth or taskcluster-confabulator.  The best way to get the current supported set of taskcluster libraries is still going to be to install the taskcluster-base npm module.&lt;br /&gt;
&lt;br /&gt;
Some of our libraries are quiet large and have a lot of history in them.  I didn't really want to just create a new repository and copy in the files we care about and destroy the history.  Instead, I wrote a simple and ugly tool (https://github.com/jhford/taskcluster-base-split) which does the pedestrian tasks involved in this split up by filtering out irrelevant history for each project, moving files around and doing some preliminary cleanup work on the new library.&lt;br /&gt;
&lt;br /&gt;
This tooling gets us 90% of the way to a split out repository, but as always, a human is required to take it the last step of the way.  Imports need to be fixed, dependencies must be verified and tests need to be fixed.  I'm also taking this opportunity to implement babel-transpiling support in as many libraries as I can.  We use babel everywhere in our application code, so it'll be nice to have it available in our platform libraries as well.  I'm using the babel-runtime package instead of requiring the direct use of babel.  The code produced by our babel setup is tested in tested using the node 0.12 binary without any wrappers at all.&lt;br /&gt;
&lt;br /&gt;
Having different libraries will introduce the risk of our projects having version number hell.  We're still going to have a taskcluster-base npm package.  This package will simply be a package.json file which specifies the supported versions of the taskcluster-lib-* packages we ship as a release and an index.js file which imports and re-exports the libraries that we provide.  If we have two libraries that have codependent changes, we can land new versions in those repositories and use taskcluster-base as the synchronizing mechanism.&lt;br /&gt;
&lt;br /&gt;
A couple of open questions that I'd love to get input on are how we should share package.json snippets and babel configurations.  We mostly have a solution for eslint, but we'd love to be able to share as much as possible in our .babelrc configuration files.  If you have a good idea for how we can do that, please get in touch!&lt;br /&gt;
&lt;br /&gt;
One of the goals in doing this is to make writing taskcluster components easier to write.  We'd love to see components written by other teams use our framework since we know it's tested to work with Taskcluster well.  It also makes it easier for the task cluster team to advise on design and maintenance concerns.&lt;br /&gt;
&lt;br /&gt;
Once a few key changes have landed, I will write a series of blog posts explaining how core taskcluster services are structured.</content:encoded>
	<dc:date>2015-10-12T13:18:48+00:00</dc:date>
	<dc:creator>Unknown</dc:creator>
</item>
<item rdf:about="https://walac.github.io/in-tree-config/">
	<title>Wander Lairson Costa: In tree tasks configuration</title>
	<link>https://walac.github.io/in-tree-config/</link>
	<content:encoded>&lt;p&gt;This post is about our plans for representing Taskcluster tasks inside
the gecko tree. &lt;a href=&quot;https://jonasfj.dk/&quot;&gt;Jonas&lt;/a&gt;,
&lt;a href=&quot;https://code.v.igoro.us/&quot;&gt;Dustin&lt;/a&gt; and I had a discussion in Berlin about this,
here I summarize what we have so far. We currently store tasks in an
&lt;a href=&quot;https://yaml.org/&quot;&gt;yaml&lt;/a&gt; file and they translate to json format using the
mach command. The syntax we have now is not the most flexible one, it is hard
to parameterize the task and very difficulty to represents tasks relationships.&lt;/p&gt;

&lt;p&gt;Let us illustrate the shortcomings with two problems we currently have.
Both apply to B2G.&lt;/p&gt;

&lt;p&gt;B2G (as in Android) has three different
&lt;a href=&quot;https://source.android.com/source/building.html#choose-a-target&quot;&gt;build variants&lt;/a&gt;:
user, userdebug and eng. Each one has slightly different task configurations.
As there is no flexible way to parameterize tasks, we end up with one different
task file for each build variant.&lt;/p&gt;

&lt;p&gt;When doing nightly builds, we must send update data to the OTA server.
We have plans to run a build task, then run the test tasks on this build,
and if all tests pass, we run a task responsible to update the OTA server.
The point is that today we have no way to represent this relationship
inside the task files.&lt;/p&gt;

&lt;p&gt;For the first problem Jonas has a prototype for
&lt;a href=&quot;https://github.com/jonasfj/json-parameterization&quot;&gt;json parameterization&lt;/a&gt;. There
were discussions on Berlin work week either we should stick with yaml files
or use Python files for task configuration. We do want to keep the syntax
declarative, which favors yaml, but storing configurations in Python files
brings much more expressiveness and flexibility, but this can result in
the same configuration hell we have with Buildbot.&lt;/p&gt;

&lt;p&gt;The second problem is more complex, and we still haven’t reached a final design.
The first question is how we describe task dependencies, top-down, i.e., we
specify which task(s) should run after a completed task, or ground up, a task
specifies which tasks it depends on. In general, we all agreed to go to a
top-down syntax, since most scenarios beg for a top down approach. Other
either should put the description of tasks relationship inside the task
files or in a separated configuration file. We would like to represent task
dependencies inside the task file, the problem is how to check what’s the
root task for the task graph. One suggestion is having a task file called
root.yml which only contain root tasks.&lt;/p&gt;</content:encoded>
	<dc:date>2015-10-09T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5338">
	<title>Selena Deckelmann: [berlin] TaskCluster Platform: A Year of Development</title>
	<link>https://www.chesnok.com/daily/2015/10/05/berlin-taskcluster-platform-a-year-of-development/</link>
	<content:encoded>&lt;p&gt;&lt;em&gt;Back in September, the TaskCluster Platform team held a workweek in Berlin to discuss upcoming feature development, focus on platform stability and monitoring and plan for the coming quarter’s work related to Release Engineering and supporting Firefox Release. These posts are documenting the many discussions we had there.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Jonas kicked off our workweek with a brief look back on the previous year of development.&lt;/p&gt;
&lt;h3&gt;Prototype to Production&lt;/h3&gt;
&lt;p&gt;In the last year, TaskCluster went from an idea with a few tasks running to running all of FirefoxOS aka B2G continuous integration, which is about 40 tasks per minute in the current environment.&lt;/p&gt;
&lt;p&gt;Architecture-wise, not a lot of major changes were made. We went from CloudAMQP to &lt;a href=&quot;https://pulse.mozilla.org/&quot;&gt;Pulse&lt;/a&gt; (in-house RabbitMQ). And shortly, Pulse itself will be &lt;a href=&quot;https://docs.google.com/document/d/1F207nMJUXXxyDNuJuoPDfFzK39RSy0gOqrYMR-21AcQ/edit&quot;&gt;moving it’s backend to CloudAMQP&lt;/a&gt;! We introduced &lt;a href=&quot;http://docs.taskcluster.net/queue/api-docs/#status&quot;&gt;task statuses&lt;/a&gt;, and then simplified them.&lt;/p&gt;
&lt;p&gt;On the implementation side, however, a lot changed. We added many features and addressed a ton of docker worker bugs. We killed Postgres and added Azure Table Storage. We rewrote the provisioner almost entirely, and moved to ES6. We learned a lot about babel-node.&lt;/p&gt;
&lt;p&gt;We introduced the first alternative to the Docker worker, the Generic worker. We for the first time had Release Engineering create a worker, the Buildbot Bridge.&lt;/p&gt;
&lt;p&gt;We have several new users of TaskCluster! Brian Anderson from Rust created a system for testing all Cargo packages for breakage against release versions. We’ve had a number of external contributors create builds for FirefoxOS devices. We’ve had a few Github-based projects jump on &lt;a href=&quot;http://github.com/taskcluster/taskcluster-github&quot;&gt;taskcluster-github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Features that go beyond BuildBot&lt;/h3&gt;
&lt;p&gt;One of the goals of creating TaskCluster was to not just get feature parity, but go beyond and support exciting, transformative features to make developer use of the CI system easier and fun.&lt;/p&gt;
&lt;p&gt;Some of the features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.taskcluster.net/workers/docker-worker/#features-interactive&quot;&gt;Interactive sessions&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Live logging (mentioned in our &lt;a href=&quot;http://docs.taskcluster.net/queue/api-docs/#createArtifact&quot;&gt;createArtifact() docs&lt;/a&gt; and visible in the &lt;a href=&quot;http://tools.taskcluster.net/task-inspector/&quot;&gt;task-inspector&lt;/a&gt; for a task) &lt;/li&gt;
&lt;li&gt;Public-first &lt;a href=&quot;http://docs.taskcluster.net/queue/api-docs/#status&quot;&gt;task statuses&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.taskcluster.net/services/index/&quot;&gt;Easy Indexing&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Storage in S3 (see &lt;a href=&quot;http://docs.taskcluster.net/queue/api-docs/#createArtifact&quot;&gt;createArtifact()&lt;/a&gt; documentation) &lt;/li&gt;
&lt;li&gt;Public first, &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;reference-style APIs&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Support for &lt;a href=&quot;https://github.com/taskcluster/testdroid-proxy/blob/master/README.md&quot;&gt;remote device lab workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Features coming in the near future to support Release&lt;/h3&gt;
&lt;p&gt;Release is a special use case that we need to support in order to take on Firefox production worload. The focus of development work in Q4 and beyond includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Secrets handling to support Release and ops workflows. In Q4, we should see secrets.taskcluster.net go into production and UI for roles-based management.&lt;/li&gt;
&lt;li&gt;Scheduling support for coalescing, SETA and cache locality. In Q4, we’re focusing on an external data solution to support coalescing and SETA.&lt;/li&gt;
&lt;li&gt;Private data hosting. In Q4, we’ll be using a roles-based solution to support these.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2015-10-05T18:38:34+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5322">
	<title>Selena Deckelmann: TaskCluster Platform: 2015Q3 Retrospective</title>
	<link>https://www.chesnok.com/daily/2015/10/05/taskcluster-platform-2015q3-retrospective/</link>
	<content:encoded>&lt;p&gt;Welcome to TaskCluster Platform’s 2015Q3 Retrospective! I’ve been managing this team this quarter and thought it would be nice to look back on what we’ve done. This report covers what we did for our quarterly goals. I’ve linked to “Publications” at the bottom of this page, and we have a &lt;a href=&quot;http://wiki.mozilla.com/TaskCluster&quot;&gt;TaskCluster Mozilla Wiki&lt;/a&gt; page that’s worth checking out.&lt;/p&gt;
&lt;h3&gt;High level accomplishments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dramatically improved stability of TaskCluster Platform for Sheriffs by fixing TreeHerder ingestion logic and regexes, adding better logging and fixing bugs in our taskcluster-vcs and mozilla-taskcluster components&lt;/li&gt;
&lt;li&gt;Created and Deployed CI builds on three major platforms:
&lt;ul&gt;
&lt;li&gt;Added Linux64 (CentOS), Mac OS X cross-compiled builds as Tier2 CI builds &lt;/li&gt;
&lt;li&gt;Completed and documented a prototype Windows 2012 builds in AWS and task configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Deployed auth.taskcluster.net, enabling better security, better support for self-service authorization and easier contributions from outside our team&lt;/li&gt;
&lt;li&gt;Added region biasing based on cost and availability of spot instances to our AWS provisioner&lt;/li&gt;
&lt;li&gt;Managed the workload of two interns, and significantly mentored a third&lt;/li&gt;
&lt;li&gt;Onboarded Selena as a new manager&lt;/li&gt;
&lt;li&gt;Held a workweek to focus attention on bringing our environment into production support of Release Engineering &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Goals, Bugs and Collaborators&lt;/h3&gt;
&lt;p&gt;We laid out our Q3 goals in &lt;a href=&quot;https://etherpad-mozilla.org/taskcluster-2015-goals&quot;&gt;this etherpad&lt;/a&gt;. Our chosen themes this quarter were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Improve operational excellence — focus on sheriff concerns, data collection, &lt;/li&gt;
&lt;li&gt;Facilitate self-serve consumption — refactoring auth and supporting roles for scopes, and &lt;/li&gt;
&lt;li&gt;Exploit opportunities to differentiate from other platforms — support for interactive sessions, docker images as artifacts, github integration and more blogging/docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We had &lt;a href=&quot;http://mzl.la/1MPhK97&quot;&gt;139 Resolved FIXED bugs&lt;/a&gt; in TaskCluster product.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Link to graph of resolved bugs&quot; src=&quot;http://i.imgur.com/TiOJ5H8.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We also resolved 7 bugs in FirefoxOS, TreeHerder and RelEng products/components.&lt;/p&gt;
&lt;p&gt;We received significant contributions from other teams: Morgan (mrrrgn) designed, created and deployed &lt;a href=&quot;https://github.com/taskcluster/taskcluster-github/&quot;&gt;taskcluster-github&lt;/a&gt;; Ted deployed &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=921040&quot;&gt;Mac OS X cross compiled builds&lt;/a&gt;; Dustin reworked the Linux TC builds to use CentOS, and resolved 11 bugs related to TaskCluster and Linux builds.&lt;/p&gt;
&lt;p&gt;An additional 9 people contributed code to core TaskCluster, intree build scripts and and task definitions: aus, rwood, rail, mshal, gerard-majax, mihneadb@gmail.com, htsai, cmanchester, and echen.&lt;/p&gt;
&lt;h3&gt;The Big Picture: TaskCluster integration into Platform Operations&lt;/h3&gt;
&lt;p&gt;Moving from B2G to Platform was a big shift. The team had already made a goal of enabling Firefox Release builds, but it wasn’t entirely clear how to accomplish that. We spent a lot of this quarter learning things from RelEng and prioritizing. The whole team spent the majority of our time supporting others use of TaskCluster through training and support, developing task configurations and resolving infrastructure problems. At the same time, we shipped docker-worker features, provisioner biasing and a new authorization system. One tricky infra issue that John and Jonas worked on early in the quarter was a strange AWS Provisioner failure that came down to an obscure missing dependency. We had a few git-related tree closures that Greg worked closely on and ultimately committed fixes to taskcluster-vcs to help resolve. Everyone spent a lot of time responding to bugs filed by the sheriffs and requests for help on IRC.&lt;/p&gt;
&lt;p&gt;It’s hard to overstate how important the Sheriff relationship and TreeHerder work was. A couple teams had the impression that TaskCluster itself was unstable. Fixing this was a joint effort across TreeHerder, Sheriffs and TaskCluster teams.&lt;/p&gt;
&lt;p&gt;When we finished, useful errors were finally being reported by tasks and starring became much more specific and actionable. We may have received a partial compliment on this from philor. The extent of artifact upload retries, for example, was made much clearer and we’ve prioritized fixing this in early Q4.&lt;/p&gt;
&lt;p&gt;Both Greg and Jonas spent many weeks meeting with Ed and Cam, designing systems, fixing issues in TaskCluster components and contributing code back to TreeHerder. These meetings also led to Jonas and Cam collaborating more on API and data design, and this work is ongoing.&lt;/p&gt;
&lt;p&gt;We had our own “intern” who was hired on as a contractor for the summer, Edgar Chen. He did some work with the docker-worker, implementing Interactive Sessions, and did analysis on our provisioner/worker efficiency. We made him give a &lt;a href=&quot;https://air.mozilla.org/edgar-chen-taskcluster-interactive-sessions/&quot;&gt;short, sweet presentation on the interactive sessions&lt;/a&gt;. Edgar is now at CMU for his sophomore year and has referred at least one friend back to Mozilla to apply for an internship next summer.&lt;/p&gt;
&lt;p&gt;Pete completed a Windows 2012 prototype build of Firefox that’s available from Try, with documentation and a completely automated process for creating AMIs. He hasn’t created &lt;a href=&quot;https://www.youtube.com/watch?v=cez1uGY5u8A&quot;&gt;a narrated video with dueling, British-English accented robot voices&lt;/a&gt; for this build yet.&lt;/p&gt;
&lt;p&gt;We also invested a great deal of time in the RelEng interns. Jonas and Greg worked with Anhad on getting him productive with TaskCluster. When Anthony arrived, we also onboarded him. Jonas worked closely to get him working on a new project, hooks.taskcluster.net. To take these two bits of work from RelEng on, I pushed TaskCluster’s roadmap for generic-worker features back a quarter and Jonas pushed his stretch goal of getting the big graph scheduler into production to Q4.&lt;/p&gt;
&lt;p&gt;We worked a great deal with other teams this quarter on taskcluster-github, supporting new Firefox and B2G builds, RRAs for the workers and generally telling Mozilla about TaskCluster.&lt;/p&gt;
&lt;p&gt;Finally, we spent a significant amount of time interviewing, and then creating a more formal interview process that includes a coding challenge and structured-interview type questions. This is still in flux, but the first two portions are being used and refined currently. Jonas, Greg and Pete spent many hours interviewing candidates.&lt;/p&gt;
&lt;h3&gt;Berlin Work Week&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;TaskCluster Platform Team in Berlin&quot; src=&quot;http://i.imgur.com/RHOLisS.jpg?1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Toward the end of the quarter, we held a workweek in Berlin to focus our next round of work on critical RelEng and Release-specific features as well as production monitoring planning. Dustin surprised us with delightful laser cut acrylic versions of the TaskCluster logo for the team! All team members reported that they benefited from being in one room to discuss key designs, get immediate code review, and demonstrate work in progress.&lt;/p&gt;
&lt;p&gt;We came out of this with 20+ detailed documents from our conversations, greater alignment on the priorities for Platform Operations and a plan for trainings and tutorials to give at Orlando. Dustin followed this up with a series of ‘TC Topics’ Vidyo sessions targeted mostly at RelEng.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.google.com/document/d/1MwLXEEer73Xj-zLMZDtFje2U068AxV0btzRvdLIYtjo/edit&quot;&gt;Our Q4 roadmap&lt;/a&gt; is focused on key RelEng features to support Release.&lt;/p&gt;
&lt;h3&gt;Publications&lt;/h3&gt;
&lt;p&gt;Our team published a few blog posts and videos this quarter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCD0odAg4RgoTDdomOx7lSuw&quot;&gt;TaskCluster YouTube channel&lt;/a&gt; with two generic worker videos &lt;/li&gt;
&lt;li&gt;On &lt;a href=&quot;http://planet.mozilla.org/taskcluster/&quot;&gt;Planet Taskcluster&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.gregarndt.com/taskcluster/2015/10/01/taskcluster-infrastructure-monitoring/&quot;&gt;Monitoring TaskCluster Infrastructure&lt;/a&gt; (garndt) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://petemoore.github.io/general/taskcluster/2015/09/30/building-firefox-for-windows-on-try-using-taskcluster.html&quot;&gt;Building Firefox for Windows 2012 on Try&lt;/a&gt; (pmoore) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.johnford.org/2015/09/taskcluster-component-loader.html&quot;&gt;TaskCluster Component Loader&lt;/a&gt; (jhford) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jonasfj.dk/2015/08/getting-started-with-taskcluster-apis-interactive-tutorials/&quot;&gt;Getting started with TaskCluster APIs&lt;/a&gt; (jonasfj) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.gregarndt.com/taskcluster/2015/08/05/demystifying-in-tree-scheduling/&quot;&gt;De-mystifying TaskCluster intree scheduling&lt;/a&gt; (garndt) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://walac.github.io/taskcluster-b2g/&quot;&gt;Running phone builds on TaskCluster&lt;/a&gt; (wcosta) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On Air Mozilla
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://air.mozilla.org/edgar-chen-taskcluster-interactive-sessions/&quot;&gt;Interactive Sessions&lt;/a&gt; (Edgar Chen) &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://air.mozilla.org/taskcluster-github-continuous-integration-for-mozillians-by-mozillians-2/&quot;&gt;TaskCluster GitHub, Continuous integration for Mozillians by Mozillians&lt;/a&gt; (mrrrgn)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2015-10-05T17:39:31+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="https://walac.github.io/taskcluster-b2g/">
	<title>Wander Lairson Costa: Running phone builds on Taskcluster</title>
	<link>https://walac.github.io/taskcluster-b2g/</link>
	<content:encoded>&lt;p&gt;In this post I am going to talk about my work for phone builds inside the
&lt;a href=&quot;https://docs.taskcluster.net/&quot;&gt;Taskcluster&lt;/a&gt; infrastructure. Mozilla is
slightly moving from Buildbot to Taskcluster. Here I am going to
give a survivor guide on Firefox OS phone builds.&lt;/p&gt;

&lt;h3&gt;Submitting tasks&lt;/h3&gt;

&lt;p&gt;A task is nothing more than a json file containing the description 
of the job to execute. But you don’t need to handle the json directly, all tasks
are written in &lt;a href=&quot;https://en.wikipedia.org/wiki/YAML&quot;&gt;YAML&lt;/a&gt;, and it is then processed
by the &lt;a href=&quot;https://mzl.la/1MkZ4gz&quot;&gt;mach&lt;/a&gt; command. The in tree tasks are located
at &lt;a href=&quot;https://mzl.la/1MkYOhw&quot;&gt;testing/taskcluster/tasks&lt;/a&gt; and the build tasks are
inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builds/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;My favorite command to try out a task is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mach taskcluster-build&lt;/code&gt; command.
It allows you to process a single task and output the json formatted task ready
for Taskcluster submission.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./mach taskcluster-build &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--head-repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://hg.mozilla.org/mozilla-central 
    &lt;span class=&quot;nt&quot;&gt;--head-rev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;tip &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--owner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;foobar@mozilla.com &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    tasks/builds/b2g_desktop_opt.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Although we specify a Mercurial repository, Taskcluster also accepts git
repositories interchangeably.&lt;/p&gt;

&lt;p&gt;This command will print out the task to the console output. To
run the task, you can copy the generated task and paste it in the
&lt;a href=&quot;https://tools.taskcluster.net/task-creator/&quot;&gt;task creator&lt;/a&gt; tool. Then just
click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Task&lt;/code&gt; to schedule it to run. Remember that you need
&lt;a href=&quot;https://auth.taskcluster.net/&quot;&gt;Taskcluster Credentials&lt;/a&gt; to run Taskcluster
tasks. If you have
&lt;a href=&quot;https://www.npmjs.com/package/taskcluster-cli/&quot;&gt;taskcluster-cli&lt;/a&gt;
installed, you can the pipe the mach output to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;taskcluster run-task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tasks are effectively executed inside a &lt;a href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;
&lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker&quot;&gt;image&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Mozharness&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://wiki.mozilla.org/ReleaseEngineering/Mozharness&quot;&gt;Mozharness&lt;/a&gt;
is what we use for effectively build stuff. Mozharness
architecture, despite its code size, is quite simple. Under the 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scripts&lt;/code&gt; directory you find the harness scripts. We are specifically
interested in the &lt;a href=&quot;https://tinyurl.com/nlm8mjm&quot;&gt;b2g_build.py&lt;/a&gt; script. As the script
name says, it is responsible for B2G builds. The B2G harness configuration
files are located at the &lt;a href=&quot;https://tinyurl.com/nzqlkfe&quot;&gt;b2g/config&lt;/a&gt; directory. Not
surprisingly, all files starting with “taskcluster” are for Taskcluster
related builds.&lt;/p&gt;

&lt;p&gt;Here are the most common configurations:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;default_vcs&lt;/dt&gt;
  &lt;dd&gt;This is the default vcs used to clone repositories when no other is given.
  [tc_vcs](https://tc-vcs.readthedocs.org/en/latest/) allows mozharness to
  clone either git or mercurial repositories transparently, with repository
  caching support.&lt;/dd&gt;
  &lt;dt&gt;default_actions&lt;/dt&gt;
  &lt;dd&gt;The actions to execute. They must be present and in the same order as
  in the build class `all_actions` attribute.&lt;/dd&gt;
  &lt;dt&gt;balrog_credentials_file&lt;/dt&gt;
  &lt;dd&gt;The credentials to send update data to the OTA server.&lt;/dd&gt;
  &lt;dt&gt;nightly_build&lt;/dt&gt;
  &lt;dd&gt;`True` if this is a nightly build.&lt;/dd&gt;
  &lt;dt&gt;upload&lt;/dt&gt;
  &lt;dd&gt;Upload info. Not used for Taskcluster.&lt;/dd&gt;
  &lt;dt&gt;repo_remote_mappings&lt;/dt&gt;
  &lt;dd&gt;Maps externals repository to [mozilla domain](https://git.mozilla.org).&lt;/dd&gt;
  &lt;dt&gt;env&lt;/dt&gt;
  &lt;dd&gt;Environment variables for commands executed inside mozharness.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The listed actions map to Python methods inside the build class, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; replaced
by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;. For example, the action &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkout-sources&lt;/code&gt; maps to the method
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkout_sources&lt;/code&gt;. That’s where the mozharness simplicity comes from: everything boils
down to a sequence of method calls, just it, no secret.&lt;/p&gt;

&lt;p&gt;For example, here is how you run mozharness to build a flame image:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python &amp;lt;gecko-dir&amp;gt;/testing/mozharness/scripts/b2g_build.py &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; b2g/taskcluster-phone.py &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--disable-mock&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--variant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;user &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--work-dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;B2G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--gaia-languages-file&lt;/span&gt; locales/languages_all.json &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--log-level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;debug &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;flame-kk &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--b2g-config-dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;flame-kk &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://hg.mozilla.org/mozilla-central &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember you need your flame connected to the machine so the build system
can extract the blobs.&lt;/p&gt;

&lt;p&gt;In general you don’t need to worry about mozharness command line because it is wrapped
by the &lt;a href=&quot;https://tinyurl.com/py798c3&quot;&gt;build scripts&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Hacking Taskcluster B2G builds&lt;/h3&gt;

&lt;p&gt;All Taskcluster tasks run inside a docker container. Desktop and emulator B2G builds
run inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder&lt;/code&gt; docker image. Phone builds are more complex, because:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Mozilla is not allowed to publicly redistribute phone binaries.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Phone build tasks need to access the &lt;a href=&quot;https://wiki.mozilla.org/Balrog&quot;&gt;Balrog&lt;/a&gt;
  server to send OTA update data.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Phone build tasks need to upload symbols to the
  &lt;a href=&quot;https://mzl.la/1Ta6jfY&quot;&gt;crash reporter&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Due to (1), only users authenticated with a @mozilla account are allowed
to download phone binaries (this works the same way as private builds). And
because of (1), (2) and (3), the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phone-builder&lt;/code&gt; docker image is secret,
so only authorized users can submit tasks to it.&lt;/p&gt;

&lt;p&gt;If you need to create a build task for a new phone, most of the time you will
starting from an existing task (Flame and Aries tasks are preferred) and then
make your customizations. You might need to add new features to the
&lt;a href=&quot;https://tinyurl.com/py798c3&quot;&gt;build scripts&lt;/a&gt;, which currently are not the most
flexible scripts around.&lt;/p&gt;

&lt;p&gt;If you need to customize mozharness, make sure your changes are Python 2.6
compatible, because mozharness is used to run Buildbot builds too, and the
Buildbot machines run Python 2.6. The best way to minimize risk of breaking
stuff is to submit your patches to try with “-p all -b do” flags.&lt;/p&gt;

&lt;p&gt;Need help? Ask at the #taskcluster channel.&lt;/p&gt;</content:encoded>
	<dc:date>2015-10-05T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://petemoore.github.io/general/taskcluster/2015/09/30/building-firefox-for-windows-on-try-using-taskcluster.html">
	<title>Pete Moore: Building Firefox for Windows™ on Try using TaskCluster</title>
	<link>http://petemoore.github.io/general/taskcluster/2015/09/30/building-firefox-for-windows-on-try-using-taskcluster.html</link>
	<content:encoded>&lt;p&gt;&lt;img alt=&quot;Firefox on Windows screenshot&quot; src=&quot;http://petemoore.github.io/assets/2014-06-20-10-38-11-106a6c.png&quot; /&gt;&lt;/p&gt;

&lt;h4&gt;Try them out for yourself!&lt;/h4&gt;

&lt;p&gt;Here are the try builds we have created. They were built from the official in-tree &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/file/default/browser/config/mozconfigs&quot;&gt;mozconfigs&lt;/a&gt; that we use for the builds running in Buildbot.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://queue.taskcluster.net/v1/task/ARNCa6tfSDuXMxkzxBjUTQ/runs/0/artifacts/public/build/firefox-43.0a1.en-US.win32.zip&quot;&gt;win32 opt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://queue.taskcluster.net/v1/task/ZbyHXDfCTeG40Khq8sDI9w/runs/0/artifacts/public/build/firefox-43.0a1.en-US.win32.zip&quot;&gt;win32 debug&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://queue.taskcluster.net/v1/task/YlS2Tx5cSTioK144E1TnOA/runs/0/artifacts/public/build/firefox-43.0a1.en-US.win64.zip&quot;&gt;win64 opt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://queue.taskcluster.net/v1/task/Ar_6p6QCQ02_xYIaMc_Dlw/runs/0/artifacts/public/build/firefox-43.0a1.en-US.win64.zip&quot;&gt;win64 debug&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Set up your own Windows™ Try tasks&lt;/h4&gt;

&lt;p&gt;We are porting over all of Mozilla’s CI tasks to &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;TaskCluster&lt;/a&gt;, including Windows™ builds and tests.&lt;/p&gt;

&lt;p&gt;Currently Windows™ and OS X tasks still run on our legacy Buildbot infrastructure. This is about to change.&lt;/p&gt;

&lt;p&gt;In this post, I am going to talk you through how I set up Firefox Desktop builds in TaskCluster on &lt;a href=&quot;https://wiki.mozilla.org/ReleaseEngineering/TryServer#Try_Server&quot;&gt;Try&lt;/a&gt;. In future, the TaskCluster builds should replace the existing Buildbot builds, even for releases. Getting them running on Try was the first in a long line of many steps.&lt;/p&gt;

&lt;p&gt;Spoiler alert: &lt;a href=&quot;https://treeherder.mozilla.org/#/jobs?repo=try&amp;amp;revision=fc4b30cc56fb&quot;&gt;https://treeherder.mozilla.org/#/jobs?repo=try&amp;amp;revision=fc4b30cc56fb&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Using the right Worker&lt;/h4&gt;

&lt;p&gt;In TaskCluster, Linux tasks run in a &lt;a href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; container. This doesn’t work on Windows, so we needed a different strategy.&lt;/p&gt;

&lt;p&gt;TaskCluster defines the role of a &lt;a href=&quot;http://docs.taskcluster.net/queue/worker-interaction/&quot;&gt;&lt;em&gt;Worker&lt;/em&gt;&lt;/a&gt; as component that is able to claim tasks from the Queue, execute them, publish artifacts, and report back status to the Queue.&lt;/p&gt;

&lt;p&gt;For Linux, we have the &lt;a href=&quot;http://docs.taskcluster.net/workers/docker-worker/&quot;&gt;Docker Worker&lt;/a&gt;. This is the component that takes care of executing Linux tasks inside a docker container. Since everything takes place in a container, consecutive tasks cannot interfere with each other, and you are guaranteed a clean environment.&lt;/p&gt;

&lt;p&gt;This year I have been working on the &lt;a href=&quot;http://taskcluster.github.io/generic-worker/&quot;&gt;Generic Worker&lt;/a&gt;. This takes care of running TaskCluster tasks on other platforms.&lt;/p&gt;

&lt;p&gt;For Windows, we have a different isolation strategy: since we cannot yet easily run inside a container, the Generic Worker will create a new Windows user for each task it runs.&lt;/p&gt;

&lt;p&gt;This user will have its own home directory, and will not have privileged access to the host OS. This means, it should not be able to make any persistent changes to the host OS that will outlive the lifetime of the task. The user only is able to affect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HKEY_CURRENT_USER&lt;/code&gt; registry settings, and write to its home folder, which are both purged after task completion.&lt;/p&gt;

&lt;p&gt;In other words, although not running in a container, the Generic Worker offers isolation to TaskCluster tasks by virtue of running each task as a different, custom created OS user with limited privileges.&lt;/p&gt;

&lt;h4&gt;Creating a Worker Type&lt;/h4&gt;

&lt;p&gt;TaskCluster considers a Worker Type as an entity which belongs to a &lt;a href=&quot;http://docs.taskcluster.net/aws-provisioner/api-docs/&quot;&gt;Provisioner&lt;/a&gt;, and represents a host environment and hardware context for running one or more Workers. This is the Worker Type that I set up:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;workerType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;win2012r2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minCapacity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;maxCapacity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scalingRatio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minPrice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;maxPrice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;canUseOndemand&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;canUseSpot&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;instanceTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;instanceType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;m3.2xlarge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;capacity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;utility&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;secrets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scopes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;userData&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;launchSpec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;regions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;region&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;us-west-2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;secrets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scopes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;userData&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;launchSpec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ImageId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ami-db657feb&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lastModified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2015-09-30T10:15:30.349Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;userData&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;launchSpec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;SecurityGroups&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rdp-only&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;secrets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scopes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Not everybody has permission to create worker types - but there again, you only really need to do this if you are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;using Windows (or anything else non-linux)&lt;/li&gt;
  &lt;li&gt;not able to use an existing worker type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you would like to create a new Worker Type, please contact the taskcluster team on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irc.mozilla.org&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#taskcluster&lt;/code&gt; channel.&lt;/p&gt;

&lt;p&gt;The Worker Type above boils down to some AWS hardware specs, and an ImageId &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ami-db657feb&lt;/code&gt;. But where did this come from?&lt;/p&gt;

&lt;h4&gt;Generating the AMI for the Worker Type&lt;/h4&gt;

&lt;p&gt;It is a Windows 2012 R2 AMI, and it was generated with &lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/worker_types/win2012r2&quot;&gt;this&lt;/a&gt; code checked in to the try branch. This is not automatically run, but is checked in for reference purposes.&lt;/p&gt;

&lt;p&gt;Here is the code. The first is a script that creates the AMI:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash -exv&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# cd into directory containing script...&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dirname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# generate a random slugid for aws client token...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# you need either go installed (https://golang.org/) and $GOPATH configured to run this,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# or alternatively download the 'slug' binary; see&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# http://taskcluster.github.io/slugid-go/#installing-command-line-tool&lt;/span&gt;
go get github.com/taskcluster/slugid-go/slug
&lt;span class=&quot;nv&quot;&gt;SLUGID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GOPATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/slug&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# aws cli docs lie, they say userdata must be base64 encoded, but cli encodes for you, so just cat it...&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;USER_DATA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;aws_userdata&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# create base ami, and apply user-data&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# filter output, to get INSTANCE_ID&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# N.B.: ami-4dbcb67d referenced below is *the* Windows 2012 Server R2 ami offered by Amazon in us-west-2 - it is nothing we have made&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# note, you'll need aws tool installed, access to the taskcluster AWS account, and your own private key file&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; us-west-2 ec2 run-instances &lt;span class=&quot;nt&quot;&gt;--image-id&lt;/span&gt; ami-4dbcb67d &lt;span class=&quot;nt&quot;&gt;--key-name&lt;/span&gt; pmoore-oregan-us-west-2 &lt;span class=&quot;nt&quot;&gt;--security-groups&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;RDP only&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--user-data&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER_DATA&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--instance-type&lt;/span&gt; c4.2xlarge &lt;span class=&quot;nt&quot;&gt;--block-device-mappings&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DeviceName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/sda1,Ebs&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'{VolumeSize=75,DeleteOnTermination=true,VolumeType=gp2}'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--instance-initiated-shutdown-behavior&lt;/span&gt; terminate &lt;span class=&quot;nt&quot;&gt;--client-token&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SLUGID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/^ *&quot;InstanceId&quot;: &quot;\(.*\)&quot;, */\1/p'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# sleep an hour, the installs take forever...&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;3600

&lt;span class=&quot;c&quot;&gt;# now capture the AMI - feel free to change the tags&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IMAGE_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; us-west-2 ec2 create-image &lt;span class=&quot;nt&quot;&gt;--instance-id&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;win2012r2 mozillabuild pmoore version &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SLUGID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--description&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;firefox desktop builds on windows - taskcluster worker - version &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SLUGID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/^ *&quot;ImageId&quot;: *&quot;\(.*\)&quot; *$/\1/p'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# TODO: now update worker type...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# You must update the existing win2012r2 worker type with the new ami id generated ($IMAGE_ID var above)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# At the moment this is a manual step! It can be automated following the docs:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# http://docs.taskcluster.net/aws-provisioner/api-docs/#workerType&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# http://docs.taskcluster.net/aws-provisioner/api-docs/#updateWorkerType&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Worker type ami to be used: '&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;' - don't forget to update https://tools.taskcluster.net/aws-provisioner/#win2012r2/edit&quot;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' !!!'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This script works by exploiting the fact that when you spawn a Windows instance in AWS, using one of the AMIs that Amazon provides, you can include a Powershell snippet for additional setup. This gets executed automatically when you spawn the instance.&lt;/p&gt;

&lt;p&gt;So we simply spawn an instance, passing through this powershell snippet, and then wait. A LONG time (an hour). And then we snapshot the image, and we have our new AMI. Simple!&lt;/p&gt;

&lt;p&gt;Here is the Powershell snippet that it uses:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;powershell&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# needed for making http requests&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;system.net.WebClient&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-com&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;shell.application&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# utility function to download a zip file and extract it&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Expand-ZIPFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NameSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copyhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# allow powershell scripts to run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-ExecutionPolicy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Unrestricted&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install chocolatey package manager&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'https://chocolatey.org/install.ps1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# download mozilla-build installer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://api.pub.build.mozilla.org/tooltool/sha512/03b4ca2bebede21a29f739165030bfc7058a461ffe38113452e976193e382d3ba6df8a48ac843b70429e23481e6327f43c86ffd88e4ce16263d072ef7e14e692&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\MozillaBuildSetup-2.0.0.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# run mozilla-build installer in silent (/S) mode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\MozillaBuildSetup-2.0.0.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ArgumentList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/S&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-wait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoNewWindow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PassThru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\MozillaBuild-2.0.0_install.log&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\MozillaBuild-2.0.0_install.err&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install Windows SDK 8.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choco&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;windows-sdk-8.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install Visual Studio community edition 2013&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choco&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;visualstudiocommunity2013&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# $client.DownloadFile(&quot;https://go.microsoft.com/fwlink/?LinkId=532495&amp;amp;clcid=0x409&quot;, &quot;C:\vs_community.exe&quot;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install June 2010 DirectX SDK for compatibility with Win XP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\DXSDK_Jun10.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# prerequisite for June 2010 DirectX SDK is to install &quot;.NET Framework 3.5 (includes .NET 2.0 and 3.0)&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Install-WindowsFeature&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NET-Framework-Core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Restart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# now run DirectX SDK installer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\DXSDK_Jun10.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ArgumentList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/U&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-wait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoNewWindow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PassThru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\directx_sdk_install.log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\directx_sdk_install.err&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install PSTools&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;md&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\PSTools&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Expand-ZIPFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-File&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\PSTools\PSTools.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Destination&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\PSTools&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://download.sysinternals.com/files/PSTools.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install nssm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Expand-ZIPFile&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-File&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\nssm-2.24.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Destination&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://www.nssm.cc/release/nssm-2.24.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# download generic-worker&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;md&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\generic-worker&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DownloadFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://github.com/taskcluster/generic-worker/releases/download/v1.0.12/generic-worker-windows-amd64.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\generic-worker\generic-worker.exe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# enable DEBUG logs for generic-worker install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# install generic-worker&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\generic-worker\generic-worker.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ArgumentList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;install --config C:\\generic-worker\\generic-worker.config&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-wait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoNewWindow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PassThru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\generic-worker\install.log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\generic-worker\install.err&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# add extra config needed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System.Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromBase64String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UEsDBAoAAAAAAA2hN0cIOIW2JwAAACcAAAAJAAAAZ2FwaS5kYXRhQUl6YVN5RC1zLW1YTDRtQnpGN0tNUmtoVENJYkcyUktuUkdYekpjUEsDBAoAAAAAACehN0cVjoCGIAAAACAAAAAVAAAAY3Jhc2gtc3RhdHMtYXBpLnRva2VuODhmZjU3ZDcxMmFlNDVkYmJlNDU3NDQ1NWZjYmNjM2VQSwMECgAAAAAANKE3RxYFa6ViAAAAYgAAABQAAABnb29nbGUtb2F1dGgtYXBpLmtleTE0NzkzNTM0MzU4Mi1qZmwwZTBwc2M3a2gxbXV0MW5mdGI3ZGUwZjFoMHJvMC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSBLdEhDRkNjMDlsdEN5SkNqQ3dIN1pKd0cKUEsDBAoAAAAAAEShN0ctdLepZAAAAGQAAAAYAAAAZ29vZ2xlLW9hdXRoLWFwaS5rZXlfYmFr77u/MTQ3OTM1MzQzNTgyLWpmbDBlMHBzYzdraDFtdXQxbmZ0YjdkZTBmMWgwcm8wLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIEt0SENGQ2MwOWx0Q3lKQ2pDd0g3Wkp3R1BLAwQKAAAAAABYoTdHJ3EEFiQAAAAkAAAADwAAAG1vemlsbGEtYXBpLmtleTNiNGQyN2RkLTcwM2QtNDA5NC04Mzk4LTRkZTJjNzYzNTA1YVBLAwQKAAAAAABkoTdHMi/H2yQAAAAkAAAAHgAAAG1vemlsbGEtZGVza3RvcC1nZW9sb2MtYXBpLmtleTdlNDBmNjhjLTc5MzgtNGM1ZC05Zjk1LWU2MTY0N2MyMTNlYlBLAwQKAAAAAABxoTdHJ3EEFiQAAAAkAAAAHQAAAG1vemlsbGEtZmVubmVjLWdlb2xvYy1hcGkua2V5M2I0ZDI3ZGQtNzAzZC00MDk0LTgzOTgtNGRlMmM3NjM1MDVhUEsDBBQAAAAIAHyhN0fa715hagAAAHMAAAANAAAAcmVsZW5nYXBpLnRva0ut9MpIck/O9M/08gyt8jT0y/Sy1Eut9CpINvYFCVZGhnhm+jh7Faa4Z4P4Br4QvkFqhCOIX56ca5CZFqiXU5VoWeaSm20S6eblE+rpXJDiFxoRVBphnFFZUmrpkphd7m4aVWXsFxQeCABQSwECHgMKAAAAAAANoTdHCDiFticAAAAnAAAACQAAAAAAAAABAAAApIEAAAAAZ2FwaS5kYXRhUEsBAh4DCgAAAAAAJ6E3RxWOgIYgAAAAIAAAABUAAAAAAAAAAQAAAKSBTgAAAGNyYXNoLXN0YXRzLWFwaS50b2tlblBLAQIeAwoAAAAAADShN0cWBWulYgAAAGIAAAAUAAAAAAAAAAEAAACkgaEAAABnb29nbGUtb2F1dGgtYXBpLmtleVBLAQIeAwoAAAAAAEShN0ctdLepZAAAAGQAAAAYAAAAAAAAAAEAAACkgTUBAABnb29nbGUtb2F1dGgtYXBpLmtleV9iYWtQSwECHgMKAAAAAABYoTdHJ3EEFiQAAAAkAAAADwAAAAAAAAABAAAApIHPAQAAbW96aWxsYS1hcGkua2V5UEsBAh4DCgAAAAAAZKE3RzIvx9skAAAAJAAAAB4AAAAAAAAAAQAAAKSBIAIAAG1vemlsbGEtZGVza3RvcC1nZW9sb2MtYXBpLmtleVBLAQIeAwoAAAAAAHGhN0cncQQWJAAAACQAAAAdAAAAAAAAAAEAAACkgYACAABtb3ppbGxhLWZlbm5lYy1nZW9sb2MtYXBpLmtleVBLAQIeAxQAAAAIAHyhN0fa715hagAAAHMAAAANAAAAAAAAAAEAAACkgd8CAAByZWxlbmdhcGkudG9rUEsFBgAAAAAIAAgAEQIAAHQDAAAAAA==&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;md&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\builds&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\builds\config.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Encoding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NameSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\builds\config.zip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\builds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copyhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\builds\config.zip&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# initial clone of mozilla-central&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\mozilla-build\python\python.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ArgumentList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\mozilla-build\python\Scripts\hg clone -u null https://hg.mozilla.org/mozilla-central C:\gecko&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-wait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-NoNewWindow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PassThru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardOutput&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\hg_initial_clone.log&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RedirectStandardError&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\hg_initial_clone.err&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/powershell&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hopefully this Powershell script is quite self-explanatory. It installs the required build tool chains for building Firefox Desktop, and then installs the parts it needs for running the Generic Worker on this instance. It sets up some additional config that is needed by the build process, and then takes an initial clone of mozilla-central, as an optimisation, so that future jobs only need to pull changes since the image was created.&lt;/p&gt;

&lt;p&gt;The caching strategy is to have a clone of mozilla-central live under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\gecko&lt;/code&gt;, which is updated with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg pull&lt;/code&gt; from mozilla central each time a job runs. Then when a task needs to pull from try, it is only ever a few commits behind, and should pull updates very quickly.&lt;/p&gt;

&lt;h4&gt;Defining Tasks&lt;/h4&gt;

&lt;p&gt;Once we have our AMI created, and we’ve published our Worker Type, we need to submit tasks to get the Provisioner to spawn instances in AWS, and execute our tasks.&lt;/p&gt;

&lt;p&gt;The next piece of the puzzle is working out how to get these jobs added to Try. Again, luckily for us, this is just a matter of in-tree config.&lt;/p&gt;

&lt;p&gt;For this, most of the magic exists in &lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_windows_base.yml&quot;&gt;testing/taskcluster/tasks/builds/firefox_windows_base.yml&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;$inherits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tasks/windows_build.yml'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build_product&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[TC]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Firefox&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{arch}}&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;({{build_type}})&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Firefox {{arch}} {{build_type}}&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ExtensionSdkDir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ExtensionSDKs&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Framework40Version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FrameworkDir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft.NET&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework64&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FrameworkDIR64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft.NET&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework64&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FrameworkVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0.30319&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FrameworkVersion64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0.30319&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;FSHARPINSTALLDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;F#&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;INCLUDE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;INCLUDE;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ATLMFC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;INCLUDE;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;shared;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;um;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;winrt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZBUILD_STATE_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Administrator&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.mozbuild&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_MSVCVERSION&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_MSVCYEAR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2013&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_TOOLS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;moztools-x64&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MSVCKEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HKLM&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SOFTWARE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Wow6432Node&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VisualStudio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SDKDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SDKMINORVER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SDKPRODUCTKEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HKLM&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SOFTWARE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Products&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SDKROOTKEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HKLM&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SOFTWARE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Installed&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Roots&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;SDKVER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VCDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VCINSTALLDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VisualStudioVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;VSINSTALLDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WIN64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WIN81SDKKEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{5247E16E-BCF8-95AB-1653-B3F8FBF8B3F1}&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WINCURVERKEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HKLM&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SOFTWARE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CurrentVersion&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WindowsSdkDir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WindowsSDK_ExecutablePath_x64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v8.1A&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NETFX&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;4.5.1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x64&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;WindowsSDK_ExecutablePath_x86&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v8.1A&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NETFX&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;4.5.1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MACHTYPE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;i686-pc-msys&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MAKE_MODE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unix&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZBUILDDIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZILLABUILD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_AUTOMATION&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_BUILD_DATE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;19770819000000&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_CRASHREPORTER_NO_REPORT&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MSYSTEM&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MINGW32&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hg&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-R&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gecko&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pull&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hg&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gecko&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;UPLOAD_HOST=localhost&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;UPLOAD_PATH=%CD%&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hg&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pull&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-r&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%GECKO_HEAD_REV%&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-u&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%GECKO_HEAD_REPOSITORY%&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MOZCONFIG=%CD%&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{mozconfig}}&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SRCSRV_ROOT=%GECKO_HEAD_REPOSITORY%&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;msys&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bash&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--login&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%CD%&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mach&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# In the next few days I plan to provide support for directory artifacts,&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# so this explicit list will no longer be needed, and you can specify the&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# following:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# -&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#   type: &quot;directory&quot;&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#   path: &quot;public\\build&quot;&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#   expires: '{{#from_now}}1 year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  This will be done in early October 2015. See&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;#  https://bugzilla.mozilla.org/show_bug.cgi?id=1209901&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.checksums&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.common.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.cppunittest.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.crashreporter-symbols.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.json&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.mochitest.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.mozinfo.json&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.reftest.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.talos.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.txt&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.web-platform.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.xpcshell.tests.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mar.exe&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mbsdiff.exe&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;sea&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.{{arch}}.installer.exe&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jsshell-{{arch}}.zip&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;test_packages.json&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;file&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{arch}}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;xpi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox-43.0a1.en-US.langpack.xpi&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;expires&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year{{/from_now}}'&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;treeherderEnv&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;staging&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;treeherder&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupSymbol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tc&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Submitted by taskcluster&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# from https://github.com/mozilla/treeherder/blob/9263d8432642c2ca9f68b301250af0ffbec27d83/ui/js/values.js#L3&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Rather then enforcing particular conventions we require that all build&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# tasks provide the &quot;build&quot; extra field to specify where the build and tests&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# files are located.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src/{{object_dir}}/dist/bin/firefox.exe&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src/{{object_dir}}/all-tests.json&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Reading through this, you see that with the exception of knowing the value of a few parameters (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{object_dir}}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{platform}}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{arch}}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{build_type}}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{mozconfig}}&lt;/code&gt;), the full set of steps that a Windows build of Firefox Desktop requires on the Worker Type we created above. In other words, you see the full system setup in the Worker Type definition, and the full set of task steps in this Task Definition - so now you know as much as I do about how to build Firefox Desktop on Windows. It all exists in-tree, and is transparent to developers.&lt;/p&gt;

&lt;p&gt;So where do these parameters come from? Well, this is just the &lt;em&gt;base&lt;/em&gt; config - we define opt and debug builds for win32 and win64 architectures. These live [here]:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_win32_opt.yml&quot;&gt;win32 opt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_win32_debug.yml&quot;&gt;win32 debug&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_win64_opt.yml&quot;&gt;win64 opt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_win64_debug.yml&quot;&gt;win64 debug&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here I will illustrate just one of them, the win32 debug build config:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;$inherits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tasks/builds/firefox_windows_base.yml'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debug'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;win32'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;windowsxp'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;object_dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;obj-i686-pc-mingw32'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mozconfig&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;browser\\config\\mozconfigs\\win32\\debug'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;treeherder&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;CommandPromptType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Cross&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;LIB&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;LIB;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ATLMFC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;LIB;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;winv6.3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;um&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x86;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;LIBPATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft.NET&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework64&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0.30319;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;LIB;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ATLMFC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;LIB;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CommonConfiguration&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Neutral;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ExtensionSDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft.VCLibs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CommonConfiguration&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;neutral;&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MOZ_MSVCBITS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;32&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Common7&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;IDE&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CommonExtensions&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TestWindow;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MSBuild&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;amd64;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;BIN&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;amd64_x86;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;BIN&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;amd64;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft.NET&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Framework64&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v4.0.30319;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VC&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VCPackages;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Common7&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;IDE;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Common7&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HTML&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Help&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Workshop;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Performance&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x64;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Visual&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Studio&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Performance&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x64;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Kits&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8.1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x86;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Program&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SDKs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v8.1A&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NETFX&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;4.5.1&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x64&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;System32;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Windows&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;System32&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Wbem;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;moztools-x64&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bin;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;7zip;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;info-zip;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;kdiff3;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozmake;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nsis-3.0b1;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nsis-2.46u;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;python;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;python&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Scripts;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;upx391w;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;wget;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mozilla-build&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yasm&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Platform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;X86&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;PreferredToolArchitecture&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x64&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TOOLCHAIN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;64-bit&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cross-compile&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This file above has defined those parameters, and provided some more task specific config too, which overlays the base config we saw before.&lt;/p&gt;

&lt;p&gt;But wait a minute… how do these tasks know to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win2012r2&lt;/code&gt; worker type we created? The answer to that is that &lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/builds/firefox_windows_base.yml&quot;&gt;testing/taskcluster/tasks/builds/firefox_windows_base.yml&lt;/a&gt; inherits from &lt;a href=&quot;https://hg.mozilla.org/try/file/fc4b30cc56fb/testing/taskcluster/tasks/windows_build.yml&quot;&gt;testing/taskcluster/tasks/windows_build.yml&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# This is the base windows task which contains the common values all windows builds must&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# provide.&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;$inherits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tasks/build.yml'&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workerType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;win2012r2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Incidentally, this then inherits in turn from the root yaml file for &lt;strong&gt;all&lt;/strong&gt; gecko builds (across &lt;em&gt;all&lt;/em&gt; gecko platforms):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# This is the &quot;base&quot; task which contains the common values all builds must&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# provide.&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;taskId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;build_slugid&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}}&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{now}}'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;deadline&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{#from_now}}24&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hours{{/from_now}}'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://todo.com/soon&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mozilla-taskcluster-maintenance@mozilla.com&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;createdForUser&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}}&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;provisionerId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;aws-provisioner-v1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedulerId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;task-graph-scheduler&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;index.gecko.v1.{{project}}.revision.linux.{{head_rev}}.{{build_name}}.{{build_type}}'&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;index.gecko.v1.{{project}}.latest.linux.{{build_name}}.{{build_type}}'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;queue:define-task:aws-provisioner-v1/build-c4-2xlarge'&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;queue:create-task:aws-provisioner-v1/build-c4-2xlarge'&lt;/span&gt;


  &lt;span class=&quot;na&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Two hours is long but covers edge cases (and matches bb based infra)&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;maxRunTime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7200&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Common environment variables for checking out gecko&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GECKO_BASE_REPOSITORY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{base_repository}}'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GECKO_HEAD_REPOSITORY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{head_repository}}'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GECKO_HEAD_REV&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{head_rev}}'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;GECKO_HEAD_REF&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{head_ref}}'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TOOLTOOL_REPO&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://git.mozilla.org/build/tooltool.git'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;TOOLTOOL_REV&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;master'&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build_product&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{build_product}}'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;rank&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pushlog_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;treeherder&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupSymbol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tc&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;groupName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Submitted by taskcluster&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;B&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So the complete inheritence chain looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tasks/build.yml
  tasks/windows_build.yml
    tasks/builds/firefox_windows_base.yml
      tasks/builds/firefox_win32_opt.yml
      tasks/builds/firefox_win64_debug.yml
      tasks/builds/firefox_win32_opt.yml
      tasks/builds/firefox_win64_debug.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4&gt;Getting the new tasks added to Try pushes&lt;/h4&gt;

&lt;p&gt;This involved adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win64&lt;/code&gt; as build platforms in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing/taskcluster/tasks/branches/base_job_flags.yml&lt;/code&gt; (previsouly taskcluster was not running any tasks for these platforms):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# List of all possible flags for each category of tests used in the case where&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &quot;all&quot; is specified.&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;aliases&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mochitests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mochitest&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;builds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;emulator&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;emulator-jb&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;emulator-kk&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;emulator-x86-kk&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;android-api-11&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linux64&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;macosx64&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;win32&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;########## &amp;lt;---- added here&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;win64&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;########## &amp;lt;---- added here&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cppunit&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;crashtest&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;crashtest-ipc&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gaia-build&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then associating these new task definitions we just created, to these new build platforms. This is done in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing/taskcluster/tasks/branches/try/job_flags.yml&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For complete sample of all build and test jobs,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# see &amp;lt;gecko&amp;gt;/testing/taskcluster/tasks/job_flags.yml&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;$inherits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tasks/branches/base_job_flags.yml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Flags specific to this branch&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;post-build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;upload-symbols&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;builds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;win32&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;platforms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;win32&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tasks/builds/firefox_win32_opt.yml&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tasks/builds/firefox_win32_debug.yml&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;win64&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;platforms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;win64&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tasks/builds/firefox_win64_opt.yml&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tasks/builds/firefox_win64_debug.yml&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;linux64_gecko&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;platforms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;b2g&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;.....&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;The above hopefully has given you a taste for what you can do yourself in TaskCluster, and specifically in Gecko, regarding setting up new jobs. By following this guide, you too should be able to schedule Windows jobs in Taskcluster, including try jobs for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Gecko&quot;&gt;Gecko&lt;/a&gt; projects.&lt;/p&gt;

&lt;p&gt;For more information about TaskCluster, see &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;docs.taskcluster.net&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2015-09-30T14:08:53+00:00</dc:date>
</item>
<item rdf:about="tag:blogger.com,1999:blog-2324292491075571582.post-5817496078568719252">
	<title>John Ford: Taskcluster Component Loader</title>
	<link>http://blog.johnford.org/2015/09/taskcluster-component-loader.html</link>
	<content:encoded>Taskcluster is the new platform for building Automation at Mozilla.  One of the coolest design decisions is that it's composed of a bunch of limited scope, interchangeable services that have well defined and enforced apis.  Examples of services are the Queue, Scheduler, Provisioner and Index.  In practice, the server-side components roughly map to a Heroku app.  Each app can have one or more web worker processes and zero or more background workers.&lt;br /&gt;
&lt;br /&gt;
Since we're building our services with the same &lt;a href=&quot;https://github.com/taskcluster/taskcluster-base&quot;&gt;base libraries&lt;/a&gt; we end up having a lot of duplicated glue code.  During a set of meetings in Berlin, Jonas and I were lamenting about how much copied, pasted and modified boilerplate was in our projects.&lt;br /&gt;
&lt;br /&gt;
Between the API definition file and the command line to launch a program invariably sits a &lt;code&gt;bin/server.js&lt;/code&gt; file for each service.  This script basically loads up our config system, loads our Azure Entity library, loads a Pulse publisher, a JSON Schema validator and a Taskcluster-base App.  Each background worker has its own &lt;code&gt;bin/something.js&lt;/code&gt; which basically has a very similar loop.  Services with unit tests have a &lt;code&gt;test/helper.js&lt;/code&gt; file which initializes the various components for testing.  Furthermore, we might have things initialize inside of a given &lt;code&gt;before()&lt;/code&gt; or &lt;code&gt;beforeEach()&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The problem with having so much boiler plate is twofold.  First, each time we modify one services's boilerplate, we are now adding maintenance complexity and risk because of that subtle difference to the other services.  We'd eventually end up with hundreds of glue files which do roughly the same thing, but accomplish it complete differently depending on which services it's in.  The second problem is that within a single project, we might load the same component ten ways in ten places, including in tests.  Having a single codepath that we can test ensures that we're always initializing the components properly.&lt;br /&gt;
&lt;br /&gt;
During a little downtime between sessions, Jonas and I came up with the idea to have a standard component loading system for taskcluster services.  Being able to rapidly iterate and discuss in person made the design go very smoothly and in the end, we were able to design something we were both happy with in about an hour or so.&lt;br /&gt;
&lt;br /&gt;
The design we took is to have two 'directories' of components.  One is the project wide set of components which has all the logic about how to build the complex things like validators and entities.  These components can optionally have dependencies.  In order to support different values for different environments, we force the main directory to declare which 'virtual dependencies' it requires.  They are declared as a list of strings.  The second level of component directory is where these 'virtual dependencies' have their value.&lt;br /&gt;
&lt;br /&gt;
Both Virtual and Concrete dependencies can either be 'flat' values or objects.  If a dependency is a &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt;, &lt;code&gt;Promise&lt;/code&gt; or an &lt;code&gt;object&lt;/code&gt; without a create property, we just give that exact value back as a resolved &lt;code&gt;Promise&lt;/code&gt;.  If the component is an &lt;code&gt;object&lt;/code&gt; with a &lt;code&gt;create&lt;/code&gt; property, we initialize the dependencies specified by the 'requires' list property, pass those values as properties on an object to the function at the 'create' property.  The value of that function's return is stored as a resolved promise.  Components can only depend on other components non-flat dependencies.&lt;br /&gt;
&lt;br /&gt;
Using code is a good way to show how this loader works:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
// lib/components.js

let loader = require('taskcluster-base').loader;
let fakeEntityLibrary = require('fake');

module.exports = loader({
  fakeEntity: {
    requires: ['connectionString'],
    setup: async deps =&amp;gt; {
      let conStr = await deps.connectionString;
      return fakeEntityLibrary.create(conStr);
    },
  },
},
['connectionString'],
);&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt; &lt;/code&gt;&lt;/pre&gt;
In this file, we're building a really simple component directory which only contains a contrived 'fakeEntity'.  This component depends on having a connection string to fully configure.  Since we want to use this code in production, development and testing, we don't want to bake configuration into this file, so we force the thing using this to itself give us a way to configure what the connection string.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
// bin/server.js
let config = require('taskcluster-base').config('development');
let loader = require('../lib/components.js');

let load = loader({
  connectionString: config.entity.connectionString,
});

let configuredFakeEntity = await load('fakeEntity')&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt; &lt;/code&gt;&lt;/pre&gt;
In this file, we're providing a simple directory that satisifies the 'virtual' dependencies we know that need to be fulfilled before initializing can happen.&lt;br /&gt;
&lt;br /&gt;
Since we're creating a dependency tree, we want to avoid having cyclic dependencies.  I've implemented a cycle checker which ensures that you cannot configure a cyclical dependency.  It doesn't rely on the call stack being exceeded from infinite recursion either!&lt;br /&gt;
&lt;br /&gt;
This is far from being the only thing that we figured out improvements for during this chat.  Two other problems that we were able to talk through were splitting out taskcluster-base and having a background worker framework.&lt;br /&gt;
&lt;br /&gt;
Currently, taskcluster-base is a monolithic library.  If you want our Entities at version 0.8.4, you must take our config at 0.8.4 and our rest system at 0.8.4.  This is great because it forces services to move all together.  This is also awful because sometimes we might need a new stats library but can't afford the time to upgrade a bunch of Entities.  It also means that if someone wants to hack on our stats module that they'll need to learn how to get our Entities unit tests to work to get a passing test run on their stats change.&lt;br /&gt;
&lt;br /&gt;
Our plan here is to make taskcluster-base a 'meta-package' which depends on a set of taskcluster components that we support working together.  Each of the libraries (entities, stats, config, api) will be split out into their own packages using git filter-branch to maintain history.  This is just a bit of simple leg work of ensuring that the splitting out goes smooth.&lt;br /&gt;
&lt;br /&gt;
The other thing we decided on was a standardized background looping framework.  A lot of background workers follow the pattern &quot;do this thing, wait one minte, do this thing again&quot;.  Instead of each service implementing this its own special way for each background worker, what we'd really like is to have a library which does all the looping magic itself.  We can even have nice things like a watch dog timer to ensure that the loop doesn't stick.&lt;br /&gt;
&lt;br /&gt;
Once the PR has landed for the loader, I'm going to be converting the provisioner to use this new loader.  This is a part of a new effort to make Taskcluster components easy to implement.  Once a bunch of these improvements have landed, I intend to write up a couple blog posts on how you can write your own Taskcluster service.</content:encoded>
	<dc:date>2015-09-30T12:56:11+00:00</dc:date>
	<dc:creator>Unknown</dc:creator>
</item>
<item rdf:about="http://jonasfj.dk/?p=435">
	<title>Jonas Finnemann Jensen: Getting Started with TaskCluster APIs (Interactive Tutorials)</title>
	<link>https://jonasfj.dk/2015/08/getting-started-with-taskcluster-apis-interactive-tutorials/</link>
	<content:encoded>&lt;p&gt;When we started building TaskCluster about a year and a half ago one of the primary goals was to provide a self-serve experience, so people could experiment and automate things without waiting for someone else to deploy new configuration. Greg Arndt (:garndt) recently wrote a blog post &lt;a href=&quot;http://blog.gregarndt.com/taskcluster/2015/08/05/demystifying-in-tree-scheduling/&quot;&gt;demystifying in-tree TaskCluster scheduling&lt;/a&gt;. The in-tree configuration allows developers to write new CI tasks to run on TaskCluster, and test these new tasks on try before landing them like any other patch.&lt;/p&gt;
&lt;p&gt;This way of developing test and build tasks by adding in-tree configuration in a patch is very powerful, and it allows anyone with try access to experiment with configuration for much of our CI pipeline in a self-serve manner. However, not all tools are best triggered from a post-commit-hook, instead it might be preferable to have direct API access when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Locating existing builds in our task index,&lt;/li&gt;
&lt;li&gt;Debugging for intermittent issues by running a specific task repeatedly, and&lt;/li&gt;
&lt;li&gt;Running tools for bisecting commits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To facilitate tools like this TaskCluster offers a series of &lt;a href=&quot;http://docs.taskcluster.net/queue/api-docs/&quot;&gt;well-documented&lt;/a&gt; REST APIs that can be access with either permanent or temporary TaskCluster credentials. We also provide client libraries for &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client&quot;&gt;Javascript (node/browser)&lt;/a&gt;, &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client.py&quot;&gt;Python&lt;/a&gt;, &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client-go&quot;&gt;Go&lt;/a&gt; and &lt;a href=&quot;https://github.com/taskcluster/taskcluster-client-java&quot;&gt;Java&lt;/a&gt;. However, being that TaskCluster is a loosely coupled set of distributed components it is not always trivial to figure out how to piece together the different APIs and features. To make these things more approachable I’ve started a series of &lt;a href=&quot;http://docs.taskcluster.net/learn/&quot;&gt;interactive tutorials&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.taskcluster.net/learn/&quot;&gt;Tutorial 1: Modern asynchronous Javascript&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.taskcluster.net/learn/authenticate/&quot;&gt;Tutorial 2: Authentication against TaskCluster&lt;/a&gt;, and,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.taskcluster.net/learn/create-task/&quot;&gt;Tutorial 3: Creating Your First Task&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these tutorials are interactive, featuring a runtime that will transpile your code with &lt;a href=&quot;https://babeljs.io/&quot;&gt;babel.js&lt;/a&gt; before running it in the browser. The runtime environment also exposes the &lt;code&gt;require&lt;/code&gt; function from a &lt;a href=&quot;http://browserify.org/&quot;&gt;browserify&lt;/a&gt; bundle containing some of my favorite npm modules, making the example editors a great place to test code snippets using taskcluster or related services.&lt;/p&gt;
&lt;p&gt;Happy hacking, and feel free submit PRs for all my spelling errors at &lt;a href=&quot;https://github.com/taskcluster/taskcluster-docs&quot;&gt;github.com/taskcluster/taskcluster-docs&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2015-08-13T22:25:13+00:00</dc:date>
	<dc:creator>jonasfj</dc:creator>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5289">
	<title>Selena Deckelmann: TaskCluster migration: about the Buildbot Bridge</title>
	<link>https://www.chesnok.com/daily/2015/06/03/taskcluster-migration-about-the-buildbot-bridge/</link>
	<content:encoded>&lt;p&gt;Back on May 7, Ben Hearsum gave a short talk about an important piece of technology supporting our transition to TaskCluster, &lt;a href=&quot;https://github.com/mozilla/buildbot-bridge&quot;&gt;the Buildbot Bridge&lt;/a&gt;. &lt;a href=&quot;https://vreplay.mozilla.com/replay/showRecordDetails.html?recId=1879&quot;&gt;A recording is available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I took some detailed notes to spread the word about how this work is enabling a great deal of important Q3 work like the Release Promotion project. Basically, the bridge allows us to separate out work that Buildbot currently runs in a somewhat monolithic way into TaskGraphs and Tasks that can be scheduled separately and independently. This decoupling is a powerful enabler for future work.&lt;/p&gt;
&lt;p&gt;Of course, you might argue that we could perform this decoupling in Buildbot.&lt;/p&gt;
&lt;p&gt;However, moving to TaskCluster means adopting a modern, distributed queue-based approach to managing incoming jobs. We will be freed of the performance tradeoffs and careful attention required when using relational databases for queue management (Buildbot uses MySQL for it’s queues, TaskCluster uses RabbitMQ and Azure). We also will be moving “decision tasks” in-tree, meaning that they will be closer to developer environments and likely easier to manage keeping developer and build system environments in sync.&lt;/p&gt;
&lt;p&gt;Here are my notes:&lt;/p&gt;
&lt;h3&gt;Why have the bridge?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Allows a graceful transition &lt;/li&gt;
&lt;li&gt;We’re in an annoying state where we can’t have dependencies between buildbot builds and taskcluster tasks. For example: we can’t move firefox linux builds into taskcluster without moving everything downstream of those also into taskcluster &lt;/li&gt;
&lt;li&gt;It’s not practical and sometimes just not possible to move everything at the same time. This let’s us reimplement buildbot schedulers as task graphs. Buildbot builds are tasks on the task graphs enabling us to change each task to be implemented by a Docker worker, a generic worker or anything we want or need at that point.&lt;/li&gt;
&lt;li&gt;One of the driving forces is the build promotion project – the funsize and anti-virus scanning and binary moving – this is going to be implemented in taskcluster tasks but the rest will be in Buildbot. We need to be able to bounce between the two.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What is the Buildbot Bridge (BBB)&lt;/h3&gt;
&lt;p&gt;BBB acts as a TC worker and provisioner and delegates all those things to BuildBot. As far as TC is concerned, BBB is doing all this work, not Buildbot itself. TC knows nothing about Buildbot.&lt;/p&gt;
&lt;p&gt;There are three services:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TC Listener: responds to things happening in TC&lt;/li&gt;
&lt;li&gt;BuildBot Listener: responds to BB events &lt;/li&gt;
&lt;li&gt;Reflector: takes care of things that can’t be done in response to events — it reclaims tasks periodically, for example. TC expects Tasks to reclaim tasks. If a Task stops reclaiming, TC considers that Task dead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BBB has a small database that associates build requests with TC taskids and runids.&lt;/p&gt;
&lt;p&gt;BBB is designed to be multihomed. It is currently deployed but not running on three Buildbot masters. We can lose an AWS region and the bridge will still function. It consumes from Pulse.&lt;/p&gt;
&lt;p&gt;The system is dependent on Pulse, SchedulerDB and Self-serve (in addition to a Buildbot master and Taskcluster).&lt;/p&gt;
&lt;h3&gt;Taskcluster Listener&lt;/h3&gt;
&lt;p&gt;Reacts to events coming from TC Pulse exchanges.&lt;/p&gt;
&lt;p&gt;Creates build requests in response to tasks becoming “pending”. When someone pushes to mozilla-central, BBB inserts BuildRequests into BB SchedulerDB. Pending jobs appear in BB. BBB cancels BuildRequests as well — can happen from timeouts, someone explicitly cancelling in TC.&lt;/p&gt;
&lt;h3&gt;Buildbot Listener&lt;/h3&gt;
&lt;p&gt;Responds to events coming from the BB Pulse exchanges.&lt;/p&gt;
&lt;p&gt;Claims a Task when builds start. Attaches BuildBot Properties to Tasks as artifacts. Has a buildslave name, information/metadata. It resolves those Tasks.&lt;/p&gt;
&lt;p&gt;Buildbot and TC don’t have a 1:1 mapping of BB statuses and TC resolution. Also needs to coordinate with Treeherder color. A short discussion happened about implementing these colors in an artifact rather than inferring them from return codes or statuses inherent to BB or TC.&lt;/p&gt;
&lt;h3&gt;Reflector&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Runs on a timer – every 60 seconds &lt;/li&gt;
&lt;li&gt;Reclaims tasks: need to do this every 30-60 minutes &lt;/li&gt;
&lt;li&gt;Cancels Tasks when a BuildRequest is cancelled on the BB side (have to troll through BB DB to detect this state if it is cancelled on the buildbot side)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Scenarios&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A successful build!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Task is created. Task in TC is pending, nothnig in BB. TCListener picks up the event and creates a BuildRequest (pending).&lt;/p&gt;
&lt;p&gt;BB creates a Build. BBListener receives buildstarted event, claims the Task.&lt;/p&gt;
&lt;p&gt;Reflector reclaims the Task while the Build is running.&lt;/p&gt;
&lt;p&gt;Build completes successfully. BBListener receives log uploaded event (build finished), reports success in TaskCluster.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build fails initially, succeeds upon retry&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(500 from hg – common reason to retry)&lt;/p&gt;
&lt;p&gt;Same through Reflector.&lt;/p&gt;
&lt;p&gt;BB fails, marked as RETRY BBListener receives log uploaded event, reports exception to Taskcluster and calls rerun Task.&lt;/p&gt;
&lt;p&gt;BB has already started a new Build TCListener receives task-pending event, updates runid, does &lt;em&gt;not&lt;/em&gt; create a new BuildRequest.&lt;/p&gt;
&lt;p&gt;Build completes successfully Buildbot Listener receives log uploaded event, reports success to TaskCluster.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Task exceeds deadline before Build starts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Task created TCListener receives task-pending event, creates BuildRequest Nothing happens. Task goes past deadline, TaskCluster cancels it. TCListener receives task-exception event, cancels BuildRequest through Self-serve&lt;/p&gt;
&lt;p&gt;QUESTIONS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TC deadline, what is it? Queue: a task past a deadline is marked as timeout/deadline exceeded&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On TH, if someone requests a rebuild twice what happens? * There is no retry/rerun, we duplicate the subgraph — where ever we retrigger, you get everything below it. You’d end up with duplicates Retries and rebuilds are separate. Rebuilds are triggered by humans, retries are &lt;em&gt;internal&lt;/em&gt; to BB. TC doesn’t have a concept of retries.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;How do we avoid duplicate reporting? TC will be considered source of truth in the future. Unsure about interim. Maybe TH can ignore duplicates since the builder names will be the same.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replacing the scheduler what does that mean exactly?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mostly moving decision tasks in-tree — practical impact: YAML files get moved into the tree &lt;/li&gt;
&lt;li&gt;Remove all scheduling from BuildBot and Hg polling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Roll-out plan&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Connected to the Alder branch currently&lt;/li&gt;
&lt;li&gt;Replacing some of the Alder schedulers with TaskGraphs&lt;/li&gt;
&lt;li&gt;All the BB Alder schedulers are disabled, and was able to get a push to generate a TaskGraph!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next steps might be release scheduling tasks, rather than merging into central. Someone else might be able to work on other CI tasks in parallel.&lt;/p&gt;</content:encoded>
	<dc:date>2015-06-03T16:59:27+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="http://www.chesnok.com/daily/?p=5306">
	<title>Selena Deckelmann: TaskCluster migration: a “hello, world” for worker task creator</title>
	<link>https://www.chesnok.com/daily/2015/06/02/taskcluster-migration-a-hello-world-for-worker-task-creator/</link>
	<content:encoded>&lt;p&gt;On June 1, 2015, Morgan and Dustin presented an introduction to configuring and testing &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;TaskCluster&lt;/a&gt; worker tasks. &lt;a href=&quot;https://vreplay.mozilla.com/replay/showRecordingExternal.html?key=7AvN2iczQYcI3lY&quot;&gt;The session was recorded&lt;/a&gt;. Their notes are also &lt;a href=&quot;https://etherpad.mozilla.org/taskcluster-hello-world&quot;&gt;available in an etherpad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The key tutorial information centered on how to set up jobs, test/run them locally and selecting appropriate worker types for jobs.&lt;/p&gt;
&lt;p&gt;This past quarter Morgan has been working on Linux Docker images and TaskCluster workers for Firefox builds. Using that work as an example, Morgan showed how to set up new jobs with Docker images. She also touched on a couple issues that remain, like sharing sensitive or encrypted information on publicly available infrastructure.&lt;/p&gt;
&lt;p&gt;A couple really nice things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can run the whole configuration locally by copy and pasting a shell script that’s output by the TaskCluster tools&lt;/li&gt;
&lt;li&gt;There are a number of predefined workers you can use, so that you’re not creating everything from scratch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dustin gave an overview of &lt;a href=&quot;https://tools.taskcluster.net/task-graph-inspector/#m_E90SLvQcOsLMl1Q-kqPA/&quot;&gt;task graphs using a specific example&lt;/a&gt;. Looking through the docs, I think the best source of documentation other than this video is probably the &lt;a href=&quot;http://docs.taskcluster.net/scheduler/api-docs/&quot;&gt;API documentation&lt;/a&gt;. The docs could use a little more narrative for context, as Dustin’s short talk about it demonstrated.&lt;/p&gt;
&lt;p&gt;The talk closed with an invitation to help write new tasks, with pointers to the &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1118394&quot;&gt;Android work Dustin’s been doing&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2015-06-02T14:57:47+00:00</dc:date>
	<dc:creator>selenamarie</dc:creator>
</item>
<item rdf:about="http://yourdomain.com/mozilla,%20ci/2015/02/23/taskcluster-update.html">
	<title>James Lal: Taskcluster Release Part 1 : Gecko</title>
	<link>http://yourdomain.com/mozilla,%20ci/2015/02/23/taskcluster-update.html</link>
	<content:encoded>&lt;p&gt;It's been awhile since my last blog post about taskcluster and I wanted to give an update...&lt;/p&gt;

&lt;h3&gt;Taskcluster + Gecko&lt;/h3&gt;

&lt;p&gt;Taskcluster is running by default on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=try&quot;&gt;try&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=b2g-inbound&quot;&gt;b2g-inbound&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=mozilla-inbound&quot;&gt;mozilla-inbound&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=fx-team&quot;&gt;fx-team&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=mozilla-central&quot;&gt;mozilla-central&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Treeherder you will see jobs run by both buildbot and taskcluster. The &quot;TC&quot; jobs are
prefixed accordingly so you can tell the difference.&lt;/p&gt;

&lt;p&gt;This is the last big step to enabling TC as the default CI for many mozilla
project. Adding new and existing branches is easily achieved with basic config changes.&lt;/p&gt;

&lt;p&gt;Why is this a great thing? Just about everything is &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/mach_commands.py&quot;&gt;in&lt;/a&gt; &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks/builds&quot;&gt;tree&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This means you can easily add new builds/tests and immediately push them
to try for testing (see the configs for &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/taskcluster/tasks/branches/try/job_flags.yml&quot;&gt;try&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding new tests and builds is easier than ever but the improvements don't stop there. Other key benefits on linux include:&lt;/p&gt;

&lt;h5&gt;We use &lt;a href=&quot;https://dxr.mozilla.org/mozilla-central/source/testing/docker&quot;&gt;docker&lt;/a&gt;&lt;/h5&gt;

&lt;p&gt;Docker enables easy cloning of CI environments.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Pull tester image&lt;/span&gt;
docker pull quay.io/mozilla/tester:0.0.14
&lt;span class=&quot;c&quot;&gt;# Run tester image shell&lt;/span&gt;
docker run -it quay.io/mozilla/tester:0.0.14 /bin/bash
&lt;span class=&quot;c&quot;&gt;# &amp;lt;copy/paste stuff from task defintions into this&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;Tests and builds are faster&lt;/h5&gt;

&lt;p&gt;Through this entire process we have been optimizing away overhead and using faster
machines which means both build (and particularly test) times are faster.&lt;/p&gt;

&lt;p&gt;(Wins look &lt;a href=&quot;https://treeherder.allizom.org/#/jobs?repo=b2g-inbound&amp;amp;revision=233af1dfa476&quot;&gt;big&lt;/a&gt; but more in future blog post)&lt;/p&gt;

&lt;h5&gt;What's missing ?&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Some tests fail due to differences in machines. When we move tests things
fail largely due to timing issues (there are a few cases left here).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrigger/cancel does not work (yet!) as of the time of writing this it has
not yet hit production but will be deployed soon.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Results currently show up only on &lt;a href=&quot;https://treeherder.allizom.org&quot;&gt;staging treeherder&lt;/a&gt;.
We will incrementally report these to production treeherder.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2015-02-23T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://yourdomain.com/mozilla,%20ci,%20gaia/2014/05/27/gaia-taskcluster-treeherder.html">
	<title>James Lal: Gaia + Taskcluster + Treeherder</title>
	<link>http://yourdomain.com/mozilla,%20ci,%20gaia/2014/05/27/gaia-taskcluster-treeherder.html</link>
	<content:encoded>&lt;h3&gt;What is this stuff?&lt;/h3&gt;

&lt;p&gt;(originally posted on dev-gaia)&lt;/p&gt;

&lt;p&gt;For some time now Gaia developers have wanted the ability to scale their tests infinitely, while reporting to a dashboard that both sheriffs and devs can monitor, and yet still maintain control over the test configurations themselves.&lt;/p&gt;

&lt;p&gt;Taskcluster &amp;amp; Treeherder let's us do this: http://treeherder-dev.allizom.org/ui/#/jobs?repo=gaia-master
Taskcluster http://docs.taskcluster.net/ drives the tests and with a small github hook allows us to configure the jobs from a json file in the tree (this will likely be a yaml file in the end) https://github.com/mozilla-b2g/gaia/blob/master/taskgraph.json&lt;/p&gt;

&lt;p&gt;Treeherder is the next generation &quot;TBPL&quot; which allows us to report results to sheriffs from external resources (meaning we can control the tests) for both a &quot;try&quot; interface (like pull requests) and branch landings.&lt;/p&gt;

&lt;p&gt;Crrently, we are very close to having green runs in treeherder, with only one intermittent and the rest green ...&lt;/p&gt;

&lt;h3&gt;How is this different then gaia-try?&lt;/h3&gt;

&lt;p&gt;Taskcluster will eventually replace &lt;em&gt;all&lt;/em&gt; buildbot run jobs (starting with linux)... we are currently in the process of moving tests over and getting treeherder ready for production.&lt;/p&gt;

&lt;p&gt;Gaia-try is run on top of buildbot and hooks into our github pull requests.. Gaia-try gives us a single set of suites that the sheriffs can look at and help keep our tree green. This should be considered &quot;production&quot;.&lt;/p&gt;

&lt;p&gt;Treeherder/taskcluster are designed to solve the issues with the current
buildbot/tbpl implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;in tree configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;complete control over the test environment with docker (meaning you can have the exact same setup locally as on TBPL!)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;artifacts for pull requests (think screenshots for failed tests,
gaia profiles, etc...)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in tree graph capabilities (for example &quot;smoketests&quot; builds by running smaller test
suites or how tests depend on builds).&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;How is this different from travis-ci?&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;we can scale on demand on any AWS hardware we like (at very low cost thanks
to spot)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;docker is used to provide a consistent test environment that may be run
locally&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;artifacts for pull requests (think screenshots for failed tests,
gaia profiles, etc...)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;logs can be any size (but still mostly &quot;live&quot;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;reports to TBPL2 (treeherder)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;When is this production ready?&lt;/h3&gt;

&lt;p&gt;taskcluster + treeherder is &lt;em&gt;not&lt;/em&gt; ready for production yet... while the tests are running this is not in a state where sheriffs can manage it (yet!). Our plan is to continue to add taskcluster test suites (and builds!) for all trees (yes gecko) and have them run in parallel with the buildbot jobs this month...&lt;/p&gt;

&lt;p&gt;I will be posting weekly updates on my blog about taskcluster/treeherder http://lightsofapollo.github.io/ and how it effects gaia (and hopefully your overall happiness)&lt;/p&gt;

&lt;h3&gt;Where are the docs??&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;http://docs.taskcluster.net/&lt;/li&gt;
&lt;li&gt;(More coming to gaia-taskcluster and gaia readme as we get closer to
production)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;WHERE IS THE CODE?&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;https://github.com/taskcluster (overall project)&lt;/li&gt;
&lt;li&gt;https://github.com/lightsofapollo/gaia-taskcluster (my current gaia intergration)&lt;/li&gt;
&lt;li&gt;https://github.com/mozilla/treeherder-service (treeherder backend)&lt;/li&gt;
&lt;li&gt;https://github.com/mozilla/treeherder-ui (treeherder frontend)&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2014-05-27T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://yourdomain.com/mozilla,%20ci/2014/03/04/taskcluster.html">
	<title>James Lal: Taskcluster - Mozilla's new test infrastructure project</title>
	<link>http://yourdomain.com/mozilla,%20ci/2014/03/04/taskcluster.html</link>
	<content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/taskcluster&quot;&gt;Taskcluster&lt;/a&gt; is not one singular entity that runs a script with output in a pretty interface or a github hook
listener, but rather a set of decoupled interfaces that enables us to
build various test infrastructures while optimizing for cost, performance and reliability. 
The focus of this post is Linux. I will have more information how this works for OSX/Window soon.&lt;/p&gt;

&lt;h3&gt;Some History&lt;/h3&gt;

&lt;p&gt;Mozilla has &lt;a href=&quot;http://dxr.mozilla.org/mozilla-central/source/&quot;&gt;quite&lt;/a&gt;
&lt;a href=&quot;https://github.com/mozilla&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;https://github.com/mozilla-b2g&quot;&gt;few&lt;/a&gt; different
code bases, most depend on &lt;a href=&quot;https://github.com/mozilla/gecko-dev&quot;&gt;gecko&lt;/a&gt;
(the heart of Firefox and FirefoxOS). 
Getting your project hooked up to our &lt;a href=&quot;https://tbpl.mozilla.org&quot;&gt;current CI
infrastructure&lt;/a&gt;
usually requires a multi-team process that takes days or more.
Historically, simply merging projects into gecko was easier than having
external repositories that depend on gecko, which our current CI cannot
easily support. &lt;/p&gt;

&lt;p&gt;It is critical to be able to see in one place (&lt;a href=&quot;https://tbpl.mozilla.org&quot;&gt;TBPL&lt;/a&gt;)
that all the projects depend on gecko are working.
Today TBPL current this process is tightly coupled to our buildbot infrastructure (which together
make up our current CI). If you really care about your project 
not breaking when a change lands in gecko, you really only have one option:
hosting your testing infrastructure under buildbot (which feeds TBPL).&lt;/p&gt;

&lt;h3&gt;Where Taskcluster comes in&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://wiki.mozilla.org/Auto-tools/Projects/Treeherder&quot;&gt;Treeherder&lt;/a&gt;
resolves the tight coupling problem by separating the reporting from the
test running process. This enables us to re-imagine our workflow and how it's
optimized. We can run tests anywhere using any kind of utility/library
assuming it gives us the proper hooks (really just logs and some
revision information) to plug results into our development workflow.&lt;/p&gt;

&lt;p&gt;A high level workflow with taskcluster looks like this:&lt;/p&gt;

&lt;p&gt;You submit some code (this can be patch or a pull request, etc...) to a
&quot;scheduler&quot; ( &lt;a href=&quot;https://github.com/lightsofapollo/gaia-taskcluster&quot;&gt;I have started on one for gaia&lt;/a&gt; )
which submits a set of tasks. Each task is run inside a &lt;a href=&quot;https://www.docker.io/&quot;&gt;docker&lt;/a&gt; container
the container's image is specified as part of your task. This means anything you can 
imagine running on linux you can directly specify in your container
(no more waiting for vm reimaging, etc...) this also means we
directly control the resources that container uses (less variance in
test) AND if something goes wrong you can download the entire
environment that test ran on locally to debug it.&lt;/p&gt;

&lt;p&gt;As tasks are completed the &lt;a href=&quot;https://github.com/taskcluster/taskcluster-queue&quot;&gt;task cluster queue&lt;/a&gt;
emits events over AMQP (think pulse) so anyone interested in the status
of tests, etc.. can hook directly into this... This enables us to post
results as they happen directly to treeherder.&lt;/p&gt;

&lt;p&gt;The initial taskcluster &lt;a href=&quot;https://github.com/taskcluster/aws-provisioner&quot;&gt;provisions&lt;/a&gt;
AWS spot nodes on demand (we have it capped to a fixed number right now) so during peaks
we can burst to an almost unlimited number of nodes. During idle times
workers shut themselves down to reduce costs. We have additional plans
for different clouds (and physical hardware on open stack).&lt;/p&gt;

&lt;p&gt;Each component can be easily replaced (and multiple types of workers and
provisioners can be added on demand. &lt;a href=&quot;http://jonasfj.dk/blog/&quot;&gt;Jonas Finnemann Jensen&lt;/a&gt; has done a awesome job
documenting how taskcluster works &lt;a href=&quot;http://docs.taskcluster.net/queue/&quot;&gt;in the docs&lt;/a&gt;
at the API level.&lt;/p&gt;

&lt;h3&gt;What the future looks like&lt;/h3&gt;

&lt;p&gt;My initial plan is to hook everything up for &lt;a href=&quot;https://github.com/mozilla-b2g/gaia&quot;&gt;gaia&lt;/a&gt;
the FirefoxOS frontend. This will replace our current travis CI setup.&lt;/p&gt;

&lt;p&gt;As pull requests come in we will run tests on taskcluster and report
status to both treeherder and github (the beloved &lt;a href=&quot;https://github.com/blog/1227-commit-status-api&quot;&gt;github status api&lt;/a&gt;).
The ability to hook up new types of tests from the tree itself (and test
new types from the tree itself) will continue on in the form of a task
template (another blog post coming). Developers can see the status of
their tests from treeherder.&lt;/p&gt;

&lt;p&gt;Code landing in master follows the same practice and results will report
into a gaia specific treeherder view.&lt;/p&gt;

&lt;p&gt;Most importantly immediately after treeherder is launched we can run all
gaia testing on &lt;em&gt;the same exact infrastructure for both gaia and gecko
commits&lt;/em&gt; &lt;a href=&quot;https://twitter.com/SickingJ&quot;&gt;Jonas Sicking (b2g overload)&lt;/a&gt;
has some great ideas about locking gecko &amp;lt;-&amp;gt; gaia versions to reduce
another kind of failure which occurs when developing against the ever
changing landscape of gecko / gaia commits.&lt;/p&gt;

&lt;p&gt;When is the future? We have implemented the &quot;core&quot; of taskcluster
already and have the ability to run tests. By the end of the month
(March) we will have the capability to replace the entire gaia workflow with
taskcluster.&lt;/p&gt;

&lt;h3&gt;Why not X CI solution&lt;/h3&gt;

&lt;p&gt;Building a brand new CI solution is non-trivial why are we doing this?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To leverage LXC containers (docker): One of the big problems we hit
when trying to debug test failures is the vairance of testing
locally and remotely. With LXC containers you can download the
entire container (the entire environment which your test runs in)
and run it with the same cpu/memory/swap/filesystem as it would
run remotely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On demand scaling. We have (somewhat predictable) bursts throughout 
the day and the ability to spin up (and down) on demand is required to 
keep up with our changing needs throughout the day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make in tree configuration easy. Pull requests + in tree configuration
enable developers to quickly iterate on tests and testing infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modular extensible components with public facing APIs. Want run
tasks to do things other then test/build or report to something other
then treeherder? &lt;a href=&quot;http://docs.taskcluster.net&quot;&gt;We have or will build an api for that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hackability is imporant... The parts you don't want to solve
(running aws nodes, keeping them up, pricing them, etc...) are
solved for you so you can focus on building the next great mozilla
related thing (better bisection tools, etc...).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More flexibility to test/deploy optimizations... We have something
like a compute year of tests and 10-30+ minute chunks of testing is
normal. We need to iterate on our test infrastructure quickly to try
to reduce this where possible with CI changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are a few potential alternatives below... I list out the pros &amp;amp;
cons of each from my perspective (and a short description of each).&lt;/p&gt;

&lt;h4&gt;Travis [hosted]&lt;/h4&gt;

&lt;p&gt;TravisCI is an awesome [free] open source testing service that we use
for many of our smaller projects. &lt;/p&gt;

&lt;p&gt;Travis works really well for the 90% webdev usecase. 
Gaia does not fit well into that use case and gecko does so even less.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dead simple setup.&lt;/li&gt;
&lt;li&gt;Iterate on test frameworks, etc... on every pull request without any
issue.&lt;/li&gt;
&lt;li&gt;Nice simple UI which reports live logging.&lt;/li&gt;
&lt;li&gt;Adding tests and configuring tests is trivial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Difficult to debug failures locally.&lt;/li&gt;
&lt;li&gt;No public facing API for creating jobs.&lt;/li&gt;
&lt;li&gt;No build artifacts on pull requests.&lt;/li&gt;
&lt;li&gt;Cannot store arbitrarily long logs (this is only an issue for open source IIRC).&lt;/li&gt;
&lt;li&gt;On demand scaling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Buildbot [build on top of it]&lt;/h4&gt;

&lt;p&gt;We currently use buildbot at scale thousands~ of machines for all gecko
testing on multiple platforms. If you are using firefox it was built by
our buildbot setup.&lt;/p&gt;

&lt;p&gt;(NOTE: This is a critique of how we currently use buildbot not the
entire project). If I am missing something or you think a CI solution
could fit the bill contact me!&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have it working at a large scale already.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding tests and configuring tests is fairly difficult and involves
long lead times.&lt;/li&gt;
&lt;li&gt;Difficult to debug failures locally.&lt;/li&gt;
&lt;li&gt;Configuration files live outside of the tree.&lt;/li&gt;
&lt;li&gt;Persistent connection master/slave model.&lt;/li&gt;
&lt;li&gt;Its one monolithic project which is difficult to replace components
of.&lt;/li&gt;
&lt;li&gt;Slow rollout of new machine requirements &amp;amp; configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Jenkins&lt;/h4&gt;

&lt;p&gt;We are using Jenkins for our on device testing.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to configure jobs from the UI (decent ability to do
configuration yourself).&lt;/li&gt;
&lt;li&gt;Configuration (by default) does not live in the tree.&lt;/li&gt;
&lt;li&gt;Tons of plugins (with varying quality).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default difficult to debug failures locally.&lt;/li&gt;
&lt;li&gt;Persistent connection master/slave model.&lt;/li&gt;
&lt;li&gt;Configuration files live outside of the tree.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Drone.io [hosted/not hosted]&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/drone/drone&quot;&gt;Drone.io&lt;/a&gt; recently open sourced... It's
docker based and shows promise. Out of all the options above it looks the 
closest to the to what we want for linux testing.&lt;/p&gt;

&lt;p&gt;I am going to omit the Pros/Cons here the basics look good for drone and
it requires some more investigation. Some missing things here
are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A long term plan for supporting multiple operating systems. &lt;/li&gt;
&lt;li&gt;A public api for scheduling tasks/jobs.&lt;/li&gt;
&lt;li&gt;On demand scaling.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
	<dc:date>2014-03-04T00:00:00+00:00</dc:date>
</item>
<item rdf:about="http://yourdomain.com/2014/01/31/docker-volumes.html">
	<title>James Lal: Using docker volumes for rapid development of containers</title>
	<link>http://yourdomain.com/2014/01/31/docker-volumes.html</link>
	<content:encoded>&lt;p&gt;Its fairly obvious how to use docker for shipping an immutable image
that is great for deployment.. It was less obvious (to me) how to use
docker to iterate on the image, run tests in it, etc...&lt;/p&gt;

&lt;p&gt;Lets say you have a node project and your writing some web service
thing:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// server.js&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'http'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// server_test.js&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'my tests'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# Dockerfile
FROM lightsofapollo/node:0.10.24
ADD . /service
WORKDIR /service
CMD node server.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Before Volumes&lt;/h3&gt;

&lt;p&gt;Without using volumes your workflow is like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;docker build -t image_name
docker run image_name ./node_modules/.bin/mocha server_test.js
# .. make some changes and repeat...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While this is certainly not awful its a lot of extra steps you probably
don't want to do...&lt;/p&gt;

&lt;h3&gt;After Volumes&lt;/h3&gt;

&lt;p&gt;While iterating ideally we could just &quot;shell in&quot; to the container and
make changes on the fly then run some tests (like lets say vagrant).&lt;/p&gt;

&lt;p&gt;You can do this with volumes:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;# Its important that you only use the -v command during development it
# will override the contents of whatever you specify and you should also
# keep in mind you want to run the final tests on the image without this
# volume at the end of your development to make sure you didn't forget to
# build or somthing.

# Mount the current directory in your service folder (override the add
# above) then open an interactive shell
docker run -v $PWD:/service -i -t /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From here you can hack like normal making changes and running tests on
the fly like you would with vagrant or on your host.&lt;/p&gt;

&lt;h3&gt;When your done!&lt;/h3&gt;

&lt;p&gt;I usually have a makefile... I would setup the &quot;make test&quot; target
something like this to ensure your tests are running on the contents of
your image rather then using the volume&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-make&quot;&gt;&lt;span class=&quot;nf&quot;&gt;.PHONY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  docker build -t my_image
  docker run my_image npm &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;.PHONY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;
  docker push my_image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded>
	<dc:date>2014-01-31T00:00:00+00:00</dc:date>
</item>

</rdf:RDF>
