<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Development and DevOps</title>
    <link>http://tylerpower.io/</link>
    <description>Recent content on Development and DevOps</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 24 Feb 2016 10:33:17 +1200</lastBuildDate>
    <atom:link href="http://tylerpower.io/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Use SSH keys with Terraform on Azure</title>
      <link>http://tylerpower.io/post/ssh-keys-with-terraform-on-azure/</link>
      <pubDate>Wed, 24 Feb 2016 10:33:17 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/ssh-keys-with-terraform-on-azure/</guid>
      <description>&lt;p&gt;Recently I&amp;rsquo;ve been using an awesome tool called &lt;a href=&#34;https://www.terraform.io&#34;&gt;Terraform&lt;/a&gt;
for provisioning temporary cloud infrastructure for performance and scalability
testing some software.&lt;/p&gt;

&lt;p&gt;I initially started with AWS and then moved on to repeating the same basic
infrastructure on Azure. Azure is a bit different from AWS in terms of its resource
types but generally maps quite well. One thing that caused an hour of head scratching
and frustration was trying to configure the virtual machines I was creating to use SSH
keys.&lt;/p&gt;

&lt;p&gt;When you create a Linux virtual machine on Azure via the &lt;a href=&#34;https://portal.azure.com&#34;&gt;Portal&lt;/a&gt;
you are given the opportunity to provide a public SSH key to use for authentication.
I expected Terraform to provide an &lt;code&gt;ssh_key&lt;/code&gt; parameter but instead found an
&lt;code&gt;ssh_key_thumbprint&lt;/code&gt; parameter. After some digging it turns out you can associate certificates
with a &amp;ldquo;hosted service&amp;rdquo; in Azure and then reference those when creating virtual machines.&lt;/p&gt;

&lt;p&gt;Terraform doesn&amp;rsquo;t provide support for uploading SSH keys so I came up with a simple solution.
Note this requires that you have the Azure CLI tool installed (and that you&amp;rsquo;ve logged in) as well
as OpenSSH.&lt;/p&gt;

&lt;p&gt;In your Terraform template take two arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;azure_ssh_key_path&lt;/code&gt; - The path to the SSH key you want to use for your virtual machines. Note it must be in &lt;code&gt;der&lt;/code&gt; format.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azure_ssh_key_fingerprint&lt;/code&gt; - The fingerprint of the SSH key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, when creating hosted services, use a local provisioner to upload the key:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;variable &amp;quot;azure_ssh_key_path&amp;quot; {}
variable &amp;quot;azure_ssh_key_fingerprint&amp;quot; {}

resource &amp;quot;azure_hosted_service&amp;quot; &amp;quot;hosted_service&amp;quot; {
  name = &amp;quot;my-hosted-service&amp;quot;
  location = &amp;quot;West US&amp;quot;
  ephemeral_contents = true
  provisioner &amp;quot;local-exec&amp;quot; {
    command = &amp;quot;azure service cert create ${self.name} ${var.azure_ssh_key_path}&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And finally, when creating a virtual machine inside the hosted service, use the thumbprint:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;resource &amp;quot;azure_instance&amp;quot; &amp;quot;client&amp;quot; {
  name = &amp;quot;my-virtual-machine&amp;quot;
  image = &amp;quot;${var.azure_image_name}&amp;quot;
  size = &amp;quot;Standard_G5&amp;quot;
  hosted_service_name = &amp;quot;${azure_hosted_service.hosted_service.name}&amp;quot;
  storage_service_name = &amp;quot;...&amp;quot;
  location = &amp;quot;West US&amp;quot;
  username = &amp;quot;my-user&amp;quot;
  ssh_key_thumbprint = &amp;quot;${var.azure_ssh_key_fingerprint}&amp;quot;
  endpoint {
    name = &amp;quot;SSH&amp;quot;
    protocol = &amp;quot;tcp&amp;quot;
    public_port = 22
    private_port = 22
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you need help getting your key in to the right format for Azure you can use this bash script:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;SSH_KEY_PATH=&amp;quot;~/.ssh/id_rsa&amp;quot;
AZURE_SSH_KEY_PATH=&amp;quot;/tmp/azure-deployer.pfx&amp;quot;
openssl req -x509 \
  -key $SSH_KEY_PATH \
  -nodes \
  -days 365 -newkey rsa:2048 \
  -out /tmp/azure-deployer.pem \
  -subj &#39;/CN=www.mydomain.com/O=MyCompany./C=US&#39;
openssl x509 \
  -outform der \
  -in /tmp/azure-deployer.pem \
  -out $AZURE_SSH_KEY_PATH
fingerprint=$(openssl x509 -fingerprint -inform der -in $AZURE_SSH_KEY_PATH | grep &amp;quot;SHA1 Fingerprint&amp;quot;)
fingerprint=&amp;quot;${fingerprint#*=}&amp;quot;
fingerprint=&amp;quot;${fingerprint//:/}&amp;quot;
AZURE_SSH_KEY_FINGERPRINT=$fingerprint
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then pass the &lt;code&gt;AZURE_SSH_KEY_PATH&lt;/code&gt; and &lt;code&gt;AZURE_SSH_KEY_FINGERPRINT&lt;/code&gt; variables to Terraform:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;terraform apply -var azure_ssh_key_path=$AZURE_SSH_KEY_PATH -var azure_ssh_key_fingerprint=$AZURE_SSH_KEY_FINGERPRINT
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Hosting a YUM repository on Amazon S3</title>
      <link>http://tylerpower.io/post/hosting-yum-repo-on-s3/</link>
      <pubDate>Sat, 09 Jan 2016 10:33:17 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/hosting-yum-repo-on-s3/</guid>
      <description>

&lt;p&gt;Up until recently at &lt;a href=&#34;http://storreduce.com&#34;&gt;StorReduce&lt;/a&gt; we had been building and
distributing our software as an RPM but we didn&amp;rsquo;t actually operate a YUM repository.
This meant that while we could give a customer a new RPM to install manually, they
couldn&amp;rsquo;t just run &lt;code&gt;yum update&lt;/code&gt; to pull down the latest version of StorReduce.&lt;/p&gt;

&lt;p&gt;We decided to start operating a YUM repository to make things easier, which meant
finding a suitable place to host it, as well as updating our build processes to
publish RPMs to it as part of our CI process, and to pull RPMs from it when we
&lt;a href=&#34;http://tylerpower.io/post/packer-build-vmware-appliance-on-linux&#34;&gt;built our appliances&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To avoid operating our own web server we turned to Amazon S3 to host the repository.
By leveraging S3&amp;rsquo;s ability to serve static content over HTTP(S) and a few simple
lines of shell script we were able to come up with a scalable and secure solution
that requires zero maintenance. In addition to this we created a simple way to support
development and production repositories, with an easy way to &amp;ldquo;promote&amp;rdquo; a specific
RPM from development to production, thereby exposing it to our end users for use.
Additional stages could be added to the workflow (e.g. perhaps introducing a QA
  repo in between dev and prod, but that will be specific to the situation).&lt;/p&gt;

&lt;p&gt;Below is a guide for reproducing this setup. I recommend running these scripts via
an automated CI tool, which will give you visibility and auditability over what was
pushed to production and when.&lt;/p&gt;

&lt;h3 id=&#34;1-create-and-configure-the-buckets:54bdc4f5a07313cfd6f7f5972f200e82&#34;&gt;1. Create and configure the buckets&lt;/h3&gt;

&lt;p&gt;In the S3 web console create two buckets, one for development and one for production:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;production-yum\
development-yum\
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next we need to ensure that the buckets are publicly accessable, so update each buckets
bucket policy to enable read-only access for everyone, making sure to substitute in
the correct bucket name:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/hosting-yum-repo-on-s3/policy.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/hosting-yum-repo-on-s3/policy.png&#34; alt=&#34;S3 Buckets&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
	&amp;quot;Version&amp;quot;: &amp;quot;2012-10-17&amp;quot;,
	&amp;quot;Statement&amp;quot;: [
		{
			&amp;quot;Sid&amp;quot;: &amp;quot;AddPerm&amp;quot;,
			&amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
			&amp;quot;Principal&amp;quot;: &amp;quot;*&amp;quot;,
			&amp;quot;Action&amp;quot;: &amp;quot;s3:GetObject&amp;quot;,
			&amp;quot;Resource&amp;quot;: &amp;quot;arn:aws:s3:::production-yum/*&amp;quot;
		}
	]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;2-create-your-repo-definition-file-and-optional-gpg-key:54bdc4f5a07313cfd6f7f5972f200e82&#34;&gt;2. Create your repo definition file (and optional GPG key)&lt;/h3&gt;

&lt;p&gt;Now we need to create the &lt;code&gt;.repo&lt;/code&gt; file that can be imported in to end users
systems to allow them to install packages from our repo.&lt;/p&gt;

&lt;p&gt;This is the time to create a GPG key if you haven&amp;rsquo;t already, I recommend following
the instructions on the Fedora wiki, specifically the
&lt;a href=&#34;https://fedoraproject.org/wiki/Creating_GPG_Keys#Creating_GPG_Keys_Using_the_Command_Line&#34;&gt;Creating GPG Keys Using the Command Line&lt;/a&gt;
and &lt;a href=&#34;https://fedoraproject.org/wiki/Creating_GPG_Keys#Exporting_a_GPG_Key_Using_the_Command_Line&#34;&gt;Exporting a GPG Key Using the Command Line&lt;/a&gt;
sections.&lt;/p&gt;

&lt;p&gt;Once you have a GPG key upload it to the root of each bucket. You can skip using
GPG keys if you&amp;rsquo;d like, although it isn&amp;rsquo;t recommended.&lt;/p&gt;

&lt;p&gt;For each of the buckets created in step 1, create a &lt;code&gt;repo-name.repo&lt;/code&gt; file in the
root of the bucket with the following contents, again making sure to substitute
in the name of your bucket/repo:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[production]
name=Production Packages  - $basearch
baseurl=https://production-yum.s3-us-west-2.amazonaws.com/$basearch/
enabled=1
gpgkey=https://production-yum.s3-us-west-2.amazonaws.com/my-gpg-key
gpgcheck=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;NOTE: If you&amp;rsquo;ve decided not to use GPG keys then delete the &lt;code&gt;gpgkey&lt;/code&gt; line above
and set &lt;code&gt;gpgcheck=0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your buckets should now look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;production-yum\
  - production.repo
  - my-gpg-key

development-yum\
  - development.repo
  - my-gpg-key
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;3-publish-your-rpms:54bdc4f5a07313cfd6f7f5972f200e82&#34;&gt;3. Publish your RPMs&lt;/h3&gt;

&lt;p&gt;In this step I&amp;rsquo;ll assume you are already producing RPMs via a tool like &lt;code&gt;rpmbuild&lt;/code&gt;.
If you want to learn how to create RPMs then the Fedora Wiki has a detailed (if a
little dense) &lt;a href=&#34;https://fedoraproject.org/wiki/How_to_create_an_RPM_package&#34;&gt;article here&lt;/a&gt;.
If you&amp;rsquo;ve opted to use GPG keys make sure you sign the RPMs when you build them.&lt;/p&gt;

&lt;p&gt;Once you have built your RPM the next step is to publish it to the repo(s) hosted
on S3. The basic idea is that you pull down the contents of the repo to your local
machine, add the new RPM, then push the updated repo contents back to S3.&lt;/p&gt;

&lt;p&gt;The trick is to use the &lt;code&gt;aws s3 sync&lt;/code&gt; command to ensure only new/modified files
are moving back and forth when pulling/pushing to S3. To make this work you should
always use a consistent folder name for the work directory. I chose to use &lt;code&gt;/tmp/$BUCKET_NAME&lt;/code&gt;
which means that for the uptime of our build server we will use a cached copy of
the repo, making RPM publishing nice and quick.&lt;/p&gt;

&lt;p&gt;Here is the script that we use, it expects to be given a source directory that
contains the output of &lt;code&gt;rpmbuild&lt;/code&gt; and a target bucket that contains the YUM
repo that it will publish the RPM to. It works regardless of if the repo has been
created before or not (by omitting the &lt;code&gt;--update&lt;/code&gt; arg).&lt;/p&gt;

&lt;p&gt;I recommend only using this script to publish to the development repo you created
in step 1, we will use a different script in step 4. to move RPMs from this development
repo to the production repo.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;publish-rpm.sh:&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./publish-rpm.sh -s /tmp/rpmbuild -t development-yum
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
# Publishes built RPMs to an s3-backed RPM repo.
set -e
if [ ! -z &amp;quot;${DEBUG}&amp;quot; ]; then
  set -x
fi

SCRIPT_DIR=$( cd &amp;quot;$( dirname &amp;quot;${BASH_SOURCE[0]}&amp;quot; )&amp;quot; &amp;amp;&amp;amp; pwd )
SRC_BASE=&amp;quot;${SCRIPT_DIR}/../..&amp;quot;

DEPENDENCIES=(&amp;quot;aws&amp;quot; &amp;quot;createrepo&amp;quot;)
REGION=&amp;quot;us-west-2&amp;quot;
SOURCE_DIR=&amp;quot;&amp;quot;
TARGET_BUCKET=&amp;quot;&amp;quot;

for dep in &amp;quot;${DEPENDENCIES[@]}&amp;quot;
do
  if [ ! $(which ${dep}) ]; then
      echo &amp;quot;${dep} must be available.&amp;quot;
      exit 1
  fi
done

while getopts &amp;quot;s:t:&amp;quot; opt; do
  case $opt in
    s) SOURCE_DIR=$OPTARG ;;
    t) TARGET_BUCKET=$OPTARG ;;
    \?)
      echo &amp;quot;Invalid option: -$OPTARG&amp;quot; &amp;gt;&amp;amp;2
      exit 1
      ;;
  esac
done

if [ -z &amp;quot;${SOURCE_DIR}&amp;quot; ]; then
  echo &amp;quot;Source directory must be specified.&amp;quot;
  exit 1
fi

if [ -z &amp;quot;${TARGET_BUCKET}&amp;quot; ]; then
  echo &amp;quot;Target bucket must be specified.&amp;quot;
  exit 1
fi

TARGET_DIR=&amp;quot;/tmp/${TARGET_BUCKET}&amp;quot;

# make sure we&#39;re operating on the latest data in the target bucket
mkdir -p $TARGET_DIR
aws --region &amp;quot;${REGION}&amp;quot; s3 sync &amp;quot;s3://${TARGET_BUCKET}&amp;quot; $TARGET_DIR

# copy the RPM in and update the repo
mkdir -pv $TARGET_DIR/x86_64/
cp -rv $SOURCE_DIR/RPMS/* $TARGET_DIR
UPDATE=&amp;quot;&amp;quot;
if [ -e &amp;quot;$TARGET_DIR/x86_64/repodata/repomd.xml&amp;quot; ]; then
  UPDATE=&amp;quot;--update &amp;quot;
fi
for a in $TARGET_DIR/x86_64 ; do createrepo -v $UPDATE --deltas $a/ ; done

# sync the repo state back to s3
aws --region &amp;quot;${REGION}&amp;quot; s3 sync $TARGET_DIR s3://$TARGET_BUCKET
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;4-promote-rpms-from-development-to-production:54bdc4f5a07313cfd6f7f5972f200e82&#34;&gt;4. Promote RPMs from development to production&lt;/h3&gt;

&lt;p&gt;Now that RPMs can be published to the development repo you have an opportunity to
test these builds internally. Once a release of a new version of your software
is tagged you will want to push the corresponding RPM to the production
repo so that your end users can access it via &lt;code&gt;yum update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the script that we use to do that. It takes the name of the RPM, the name
of the bucket to copy it from and the name of the bucket to copy it to.&lt;/p&gt;

&lt;p&gt;This script is quite similar to the previous script, except that it knows to fetch
the source RPM from the source bucket/repo instead of the local file system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;promote-rpm.sh:&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./promote-rpm.sh -r my-rpm-1.0.1 -s development-yum -t production-yum
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
# Promotes an RPM from one repo to another (e.g. dev -&amp;gt; prod)
set -e
if [ ! -z &amp;quot;${DEBUG}&amp;quot; ]; then
  set -x
fi

SCRIPT_DIR=$( cd &amp;quot;$( dirname &amp;quot;${BASH_SOURCE[0]}&amp;quot; )&amp;quot; &amp;amp;&amp;amp; pwd )
SRC_BASE=&amp;quot;${SCRIPT_DIR}/../..&amp;quot;

DEPENDENCIES=(&amp;quot;aws&amp;quot; &amp;quot;createrepo&amp;quot;)
REGION=&amp;quot;us-west-2&amp;quot;

while getopts &amp;quot;s:t:r:&amp;quot; opt; do
  case $opt in
    r) RPM_MATCH=$OPTARG ;;
    s) SOURCE_BUCKET=$OPTARG ;;
    t) TARGET_BUCKET=$OPTARG ;;
    \?)
      echo &amp;quot;Invalid option: -$OPTARG&amp;quot; &amp;gt;&amp;amp;2
      exit 1
      ;;
  esac
done

for dep in &amp;quot;${DEPENDENCIES[@]}&amp;quot;
do
  if [ ! $(which ${dep}) ]; then
      echo &amp;quot;${dep} must be available.&amp;quot;
      exit 1
  fi
done

if [ -z &amp;quot;${RPM_MATCH}&amp;quot; ]; then
  echo &amp;quot;RPM match string must be specified.&amp;quot;
  exit 1
fi

if [ -z &amp;quot;${SOURCE_BUCKET}&amp;quot; ]; then
  echo &amp;quot;Source bucket must be specified.&amp;quot;
  exit 1
fi

if [ -z &amp;quot;${TARGET_BUCKET}&amp;quot; ]; then
  echo &amp;quot;Target bucket must be specified.&amp;quot;
  exit 1
fi

SOURCE_DIR=&amp;quot;/tmp/${SOURCE_BUCKET}&amp;quot;
TARGET_DIR=&amp;quot;/tmp/${TARGET_BUCKET}&amp;quot;

# make sure we&#39;re operating on the latest data in the source bucket
mkdir -p $SOURCE_DIR
aws --region &amp;quot;${REGION}&amp;quot; s3 sync &amp;quot;s3://${SOURCE_BUCKET}&amp;quot; $SOURCE_DIR

# make sure we&#39;re operating on the latest data in the target bucket
mkdir -p $TARGET_DIR
aws --region &amp;quot;${REGION}&amp;quot; s3 sync &amp;quot;s3://${TARGET_BUCKET}&amp;quot; $TARGET_DIR

# copy the RPM in and update the repo
mkdir -pv $TARGET_DIR/x86_64/
cp -rv $SOURCE_DIR/x86_64/$RPM_MATCH-1.x86_64.rpm $TARGET_DIR/x86_64/
UPDATE=&amp;quot;&amp;quot;
if [ -e &amp;quot;${TARGET_DIR}/noarch/repodata/repomd.xml&amp;quot; ]; then
  UPDATE=&amp;quot;--update &amp;quot;
fi
for a in $TARGET_DIR/x86_64 ; do createrepo -v $UPDATE --deltas $a/ ; done

# sync the repo state back to s3
aws --region &amp;quot;${REGION}&amp;quot; s3 sync $TARGET_DIR s3://$TARGET_BUCKET
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;5-test-the-new-repos:54bdc4f5a07313cfd6f7f5972f200e82&#34;&gt;5. Test the new repos&lt;/h3&gt;

&lt;p&gt;This step is nice and simple and is exactly what end users will need to do to
install or update RPMs from the new repo.&lt;/p&gt;

&lt;p&gt;Install the new repo on a client machine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo wget https://production-yum.s3-us-west-2.amazonaws.com/production.repo -O /etc/yum.repos.d/production.repo
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then start installing or updating packages from the repo:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo yum install &amp;lt;my-package-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There you have it, a secure and scalable YUM repo hosted on S3 that requires no
servers or maintenance.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Build VMware Appliances with Packer on Linux</title>
      <link>http://tylerpower.io/post/packer-build-vmware-appliance-on-linux/</link>
      <pubDate>Sun, 20 Sep 2015 11:11:06 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/packer-build-vmware-appliance-on-linux/</guid>
      <description>

&lt;p&gt;Recently I set up automated VMware virtual appliance builds using Packer and Ansible.
I had manually tested and pieced together the various steps on OS X, and thought
everything would Just Work&amp;trade; when I transferred it over to Linux to run on
our automated build infrastructure. I was wrong&amp;hellip;&lt;/p&gt;

&lt;p&gt;It turns out building on Linux can be a painfully cryptic experience. To begin with,
it&amp;rsquo;s not well documented what you actually &lt;em&gt;need to install&lt;/em&gt; to get a functioning set up,
and even once you have a set up you expect to work the various VMware components
can often return generic error messages which Packer unhelpfully parrots. You
frequently have to resort to installing a desktop environment to get the real
error message via a dialog in the VMware Workstation UI. Below is a brain dump to
help anyone else out that finds themselves with a similar need. It assumes you&amp;rsquo;re
already familiar with Packer and have a VMware based build ready to run on Linux.&lt;/p&gt;

&lt;h3 id=&#34;requirements:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;Requirements&lt;/h3&gt;

&lt;p&gt;Firstly, I &lt;em&gt;never&lt;/em&gt; got VMware Player to work on Linux, despite the odd scrap of
Packer documentation that might suggest otherwise. I found various GitHub issues where
others had the same experience. So the first requirement is to use &lt;strong&gt;&lt;a href=&#34;https://my.vmware.com/web/vmware/info?slug=desktop_end_user_computing/vmware_workstation/10_0&#34;&gt;VMware Workstation&lt;/a&gt;&lt;/strong&gt;
(specifically I use version 10).&lt;/p&gt;

&lt;p&gt;Secondly, you will need a &lt;em&gt;valid license key&lt;/em&gt; for VMware Workstation.&lt;/p&gt;

&lt;p&gt;Next up, you need to install the &lt;a href=&#34;https://my.vmware.com/web/vmware/details?downloadGroup=VIXAPI114&amp;amp;productId=489&#34;&gt;VMware Vix&lt;/a&gt;
component. You need the version that corresponds to the version of Workstation you&amp;rsquo;re using.
I used version 1.14.2 which corresponds to VMware Workstation 10.&lt;/p&gt;

&lt;p&gt;Finally, if you want to distribute a virtual appliance that can be loaded on any version
of the desktop apps (Workstation, Player, Fusion etc.) as well as the server offerings
(vSphere, ESXi etc.) then you will want to produce an OVA (single-file OVF, effectively)
instead of a VMX file, so you need to install &lt;a href=&#34;https://www.vmware.com/support/developer/ovf/&#34;&gt;OVF Tool&lt;/a&gt;.
You can grab the latest version of this, independently of what Workstation version you use.&lt;/p&gt;

&lt;h3 id=&#34;automation:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;Automation&lt;/h3&gt;

&lt;p&gt;If you&amp;rsquo;re like me you want to automate the provisioning of build servers. Here is
the Ansible role I use to install VMware on Ubuntu 14.04 ready for appliance builds.
Even if you&amp;rsquo;re not interested in Ansible you can follow the steps manually in your shell:&lt;/p&gt;

&lt;p&gt;vars/main.yml:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;vmware:
  workstation_key: &amp;quot;VALID LICENSE KEY HERE&amp;quot;
  workstation_url: &amp;quot;https://&amp;lt;your file server here&amp;gt;/VMware-Workstation-Full-10.0.6-2700073.x86_64.bundle&amp;quot;
  workstation_download_path: &amp;quot;/tmp/workstation.bundle&amp;quot;
  vix_url: &amp;quot;https://&amp;lt;your file server here&amp;gt;/VMware-VIX-1.14.2-2780323.x86_64.bundle&amp;quot;
  vix_download_path: &amp;quot;/tmp/vix.bundle&amp;quot;
  ovftool_url: &amp;quot;https://&amp;lt;your file server here&amp;gt;/VMware-ovftool-4.1.0-2459827-lin.x86_64.bundle&amp;quot;
  ovftool_download_path: &amp;quot;/tmp/ovftool.bundle&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;tasks/main.yml:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;---
- name: Install Packages
  apt: pkg={{item}} state=installed
  with_items:
    - git
    - qemu-utils
    - build-essential
    - libxtst6
    - libxss1
    - libxcursor1
    - libxinerama1
    - libxi6

- name: Download VMware Workstation
  get_url: url={{ vmware.workstation_url }} dest={{ vmware.workstation_download_path }} mode=755

- name: Download VMware VIX
  get_url: url={{ vmware.vix_url }} dest={{ vmware.vix_download_path }} mode=755

- name: Download VMware OVFTool
  get_url: url={{ vmware.ovftool_url }} dest={{ vmware.ovftool_download_path }} mode=755

- name: Install VMware Workstation
  shell: &amp;quot;{{ vmware.workstation_download_path }} --eulas-agreed --required&amp;quot;

- name: Licence VMware Workstation
  shell: /usr/lib/vmware/bin/vmware-vmx --new-sn {{ vmware.workstation_key }}

- name: Install VMware VIX
  shell: &amp;quot;{{ vmware.vix_download_path }} --eulas-agreed --required&amp;quot;

- name: Install VMware OVFTool
  shell: &amp;quot;{{ vmware.ovftool_download_path }} --eulas-agreed --required&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;building-ovas:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;Building OVAs&lt;/h3&gt;

&lt;p&gt;By default Packer will produce a VMX based virtual machine (via the vmware-vmx builder).
If you want to package this in to a single file for easy distribution, or you want
to make sure it can be used on vSphere, then you need to convert it to an OVA.&lt;/p&gt;

&lt;p&gt;There is a community provided post-processor for Packer &lt;a href=&#34;https://github.com/iancmcc/packer-post-processor-ovftool&#34;&gt;here&lt;/a&gt;
that you can then invoke from your packer.json file. I opted not to use this, as
I didn&amp;rsquo;t a) want to install golang on my build servers just to compile/install this
or b) didn&amp;rsquo;t want to manage my own pre-compiled binaries for it. Instead I chose to
use a shell script to call OVF Tool after Packer has finished.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s an example of that script - I&amp;rsquo;ve left some of the skeleton of our
main build-vm.sh script in place, so that you can see how we drive Packer and
convert the VMX to an OVA if appropriate.&lt;/p&gt;

&lt;p&gt;build-vm.sh:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/bin/bash

# Do your domain-specific steps here (e.g. accept args for different infrastructures
# and set certain environment variables for Packer to use.)

# ...

# infrastructure specific options and steps
case &amp;quot;$INFRASTRUCTURE&amp;quot; in
  &amp;quot;$INFRA_VMWARE&amp;quot;)
    export PACKER_BUILDER=&amp;quot;vmware-vmx&amp;quot;
    # ...
  ;;
  &amp;quot;$INFRA_AMAZON&amp;quot;)
    export PACKER_BUILDER=&amp;quot;amazon-ebs&amp;quot;
    # ...
  ;;
  # ...
esac

# build the vm via packer
cd &amp;quot;${SRC_BASE}/build/packer-storreduce&amp;quot; &amp;amp;&amp;amp; packer build -only $PACKER_BUILDER storreduce.json

# special case for vmware builds - convert to ova AFTER packer has run
if [[ &amp;quot;${INFRASTRUCTURE}&amp;quot; == &amp;quot;${INFRA_VMWARE}&amp;quot; ]]; then
  cd &amp;quot;${SCRIPT_DIR}&amp;quot; &amp;amp;&amp;amp; ./vmx-to-ova.sh -s &amp;quot;${PACKER_OUTPUT_DIRECTORY}&amp;quot;
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;vmx-to-ova.sh&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/bin/bash

set -e
if [ ! -z &amp;quot;${DEBUG}&amp;quot; ]; then
  set -x
fi

DEPENDENCIES=(&amp;quot;ovftool&amp;quot;)
for dep in &amp;quot;${DEPENDENCIES[@]}&amp;quot;
do
  if [ ! $(which ${dep}) ]; then
      echo &amp;quot;${dep} must be available.&amp;quot;
      exit 1
  fi
done

print_usage () {
  echo &amp;quot;vmx-to-ova.sh - Converts a VMX to an OVA and deletes the VMX if successful.&amp;quot;
  echo &amp;quot;-s=&amp;lt;source_vmx_folder&amp;gt;    The directory that contains the VMX.&amp;quot;
}

while getopts &amp;quot;s:&amp;quot; opt; do
  case $opt in
    s) SOURCE_DIR=$OPTARG ;;
    \?)
      echo &amp;quot;Invalid option: -$OPTARG&amp;quot; &amp;gt;&amp;amp;2
      print_usage
      exit 1
      ;;
  esac
done

if [[ -z &amp;quot;${SOURCE_DIR}&amp;quot; ]]; then
  echo &amp;quot;Source VMX folder must be specified&amp;quot;
  print_usage
  exit 1
fi

for vmx in &amp;quot;$SOURCE_DIR&amp;quot;/*.vmx; do
  name=$(basename &amp;quot;${vmx}&amp;quot; .vmx)
  ovftool -dm=thin --compress=1 &amp;quot;${vmx}&amp;quot; &amp;quot;${SOURCE_DIR}/${name}.ova&amp;quot;
done

cd &amp;quot;${SOURCE_DIR}&amp;quot; &amp;amp;&amp;amp; rm *.vmdk *.vmx *.vmxf *.vmsd *.nvram
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;troubleshooting:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;Troubleshooting&lt;/h3&gt;

&lt;h4 id=&#34;the-virtual-machine-has-exited:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;The virtual machine has exited.&lt;/h4&gt;

&lt;p&gt;At some point you&amp;rsquo;ll probably see this horrifically unhelpful error message in the Packer logs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;The virtual machine has exited.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This comes from VMware, and it can mean a wide variety of things. These are some
of the things to check before falling back to the last resort:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Did you license VMware Workstation with a valid license key?&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Does the host machine you&amp;rsquo;re building on have enough memory to support the VM
you&amp;rsquo;re trying to start?&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Does the host machine you&amp;rsquo;re building on have enough CPU to support the VM
you&amp;rsquo;re trying to start?&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Does the machine you&amp;rsquo;re building on have VT-X enabled? e.g. If you&amp;rsquo;re building
inside a nested virtual machine you will need to enable nested virtualization. Don&amp;rsquo;t
even bother to try on public clouds like AWS or GCE etc. as they don&amp;rsquo;t enable this.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Can&amp;rsquo;t think of anything you&amp;rsquo;ve done wrong? Then sadly the last resort is to install
a desktop environment and attempt to manually start the VM through the VMware Workstation
UI - you&amp;rsquo;ll get the real error message in a pop up dialog.&lt;/p&gt;

&lt;h4 id=&#34;failed-to-build-vmnet-failed-to-execute-the-build-command:1cfc7aec80acc474f377dd5cec495dd3&#34;&gt;Failed to build vmnet. Failed to execute the build command.&lt;/h4&gt;

&lt;p&gt;This one only recently started happening to me with the release of kernel 3.19.
I initially received the generic error message above and after falling back to
installing a desktop environment and starting the Workstation UI was prompted to
recompile VMware kernel modules for the new kernel. After clicking OK and waiting
a while the process failed with this message:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Failed to build vmnet. Failed to execute the build command.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some Googling lead me to this &lt;a href=&#34;http://askubuntu.com/questions/617704/failed-to-build-vmnet-for-kernel-3-19&#34;&gt;helpful thread&lt;/a&gt;.
I followed the instructions in &lt;a href=&#34;http://askubuntu.com/a/630360&#34;&gt;this answer&lt;/a&gt;
(after making sure I was happy with the content of the patch) and that brought VMware
back to life.&lt;/p&gt;

&lt;p&gt;Adding this step to my Ansible role is currently on my TODO list.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SSH Keys with Docker and TeamCity</title>
      <link>http://tylerpower.io/post/ssh-keys-with-docker-and-teamcity/</link>
      <pubDate>Sat, 19 Sep 2015 10:33:17 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/ssh-keys-with-docker-and-teamcity/</guid>
      <description>&lt;p&gt;Lately I&amp;rsquo;ve been configuring &lt;a href=&#34;http://tylerpower.io/post/teamcity-with-docker/&#34;&gt;TeamCity to run builds inside of Docker containers&lt;/a&gt;.
One of the classes of builds I&amp;rsquo;ve been configuring is nightly integration testing.
These tests use &lt;a href=&#34;http://www.ansible.com/&#34;&gt;Ansible&lt;/a&gt; to provision a variety of cloud
resources including servers, storage backends and load balancers, and then install
and configure our software and test it behaves as expected in real-world scenarios
(automated cluster failover testing, for example). Due to the nature of these tests
they need to be able to SSH in to the provisioned servers - which means they need
SSH key access. TeamCity has this covered.&lt;/p&gt;

&lt;p&gt;TeamCity has SSH key management built in - you simply upload your key to TeamCity
via the web interface, and then on a per-build basis you can tell it to push
the key to the build agent, and have it made available via the local SSH agent for
the duration of the build. This is exactly what I needed, except out of the box it
doesn&amp;rsquo;t work with the Docker plugin. Luckily for us the underlying mechanism
TeamCity uses to load the key is the local SSH agent, so we just need to make sure the
host&amp;rsquo;s SSH agent is available within the build&amp;rsquo;s Docker container.&lt;/p&gt;

&lt;p&gt;This turned out to be incredibly simple. The trick is to mount the local SSH agent
within the Docker container via the &lt;code&gt;-v&lt;/code&gt; flag, then to expose the mounted SSH
agent within the Docker container via the &lt;code&gt;SSH_AUTH_SOCK&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;TeamCity Docker Step Config:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-v &amp;quot;$SSH_AUTH_SOCK:/tmp/ssh_auth_sock&amp;quot;
-e &amp;quot;SSH_AUTH_SOCK=/tmp/ssh_auth_sock&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/ssh-keys-with-docker-and-teamcity/ssh-agent-mount.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/ssh-keys-with-docker-and-teamcity/ssh-agent-mount.png&#34; alt=&#34;TeamCity SSH Agent Mount Configuration&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After making this tiny configuration change you can then use the SSH Agent build
feature just as you normally would, and your Dockerized builds will inherit the SSH
key access!&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/ssh-keys-with-docker-and-teamcity/ssh-agent-feature.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/ssh-keys-with-docker-and-teamcity/ssh-agent-feature.png&#34; alt=&#34;TeamCity SSH Agent Mount Feature&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>About</title>
      <link>http://tylerpower.io/about/</link>
      <pubDate>Fri, 18 Sep 2015 18:23:05 +1200</pubDate>
      
      <guid>http://tylerpower.io/about/</guid>
      <description>&lt;p&gt;Hello I&amp;rsquo;m Tyler. I&amp;rsquo;m an entrepreneur and software engineer. I founded Appsecute
which was &lt;a href=&#34;http://techcrunch.com/2013/06/04/activestate-buys-appsecute-to-enhance-paas-with-facebook-style-feed-for-developers-and-it-pros/&#34;&gt;acquired by ActiveState&lt;/a&gt;, then worked on Stackato through to its
&lt;a href=&#34;http://venturebeat.com/2015/07/28/hp-acquires-activestates-stackato-paas-business/&#34;&gt;acquisition by HP&lt;/a&gt; and now I&amp;rsquo;m helping to disrupt Cloud Storage at &lt;a href=&#34;http://storreduce.com/&#34;&gt;StorReduce&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://twitter.com/Tyler_Power&#34;&gt;Follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://nz.linkedin.com/in/tylerwpower&#34;&gt;Connect with me on LinkedIn&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>CI with Docker and TeamCity</title>
      <link>http://tylerpower.io/post/teamcity-with-docker/</link>
      <pubDate>Fri, 18 Sep 2015 18:23:05 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/teamcity-with-docker/</guid>
      <description>&lt;p&gt;Recently I&amp;rsquo;ve been working on setting up the build infrastructure for &lt;a href=&#34;http://storreduce.com/&#34;&gt;StorReduce&lt;/a&gt;.
We have a diverse range of test, build and deployment scenarios including unit testing,
integration testing, producing binaries and RPM&amp;rsquo;s, producing virtual appliances for
various infrastructures and deploying our website, and I expect this to grow in the future.&lt;/p&gt;

&lt;p&gt;I decided to use my go-to Continuous Integration (CI) solution - &lt;a href=&#34;https://www.jetbrains.com/teamcity/&#34;&gt;TeamCity&lt;/a&gt;.
I&amp;rsquo;ve only ever had good experiences with TeamCity - it&amp;rsquo;s incredibly mature and it continues to get
better with age, it&amp;rsquo;s reliable and highly configurable and it Just Works&amp;trade;.&lt;/p&gt;

&lt;p&gt;Given the diverse range of builds we want to push through TeamCity it was desirable to
be able to compartmentalize the software on the build servers. It can quickly get out of hand having
to ensure a growing list of requirements (golang and tooling, hugo, aws cli, rpmbuild, test utils,
debian packaging tools, ansible, python&amp;hellip; etc.) are configured and installed correctly on each
build server in a CI pipeline. That&amp;rsquo;s where &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;Docker and CI are a match made in heaven. Using Docker, each build in the pipeline can be run
in a tailor-made, isolated and consistent environment every single time. Each build server
can be configured to have the bare minimum requirements installed (OS, build agent software and Docker)
and the rest can be pulled in via a Docker image based on the build that is running. This makes the
build infrastructure much easier to provision and maintain.&lt;/p&gt;

&lt;p&gt;Unfortunately, Docker is not natively supported in TeamCity, however, there is a brilliant
open source plugin called &lt;a href=&#34;https://github.com/jonnyzzz/TeamCity.Virtual&#34;&gt;TeamCity.Virtual&lt;/a&gt;
that adds a Docker target to TeamCity builds. To install the plugin follow the simple
&lt;a href=&#34;https://confluence.jetbrains.com/display/TCD9/Installing+Additional+Plugins&#34;&gt;instructions here&lt;/a&gt;.
Additionally, you will need to ensure Docker is installed on each of your build servers.&lt;/p&gt;

&lt;p&gt;Once the TeamCity.Virtual plugin is installed TeamCity will start showing a new build step:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/teamcity-with-docker/build-step.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/teamcity-with-docker/build-step.png&#34; alt=&#34;TeamCity Docker Build Step&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And after selecting it you can specify which Docker image to use, as well as the command
to run:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/teamcity-with-docker/build-step-docker.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/teamcity-with-docker/build-step-docker.png&#34; alt=&#34;TeamCity Docker Build Step Configuration&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the build above, I mount the TeamCity home directory in to the Docker container via the
-v flag, as our build script needs to run git commands to determine the version of our software
(our versioning is managed via git tags), and in TeamCity the git checkout lives in the
home directory.&lt;/p&gt;

&lt;p&gt;The final link in the chain is to ensure you have a Docker image suited to the build you&amp;rsquo;re configuring.
The build pictured is our RPM build, it compiles our binaries and produces an RPM. To do this
we need to ensure rpmbuild is installed, as well as golang and supporting tools. We then push our built
Docker image to the Docker Hub for access during the build. One of the nice things about
the TeamCity.Virtual plugin is that it does a &lt;em&gt;docker pull&lt;/em&gt; at the beginning of the build, so it will
always pull down the latest version of your Docker images.&lt;/p&gt;

&lt;p&gt;Here is the Docker file used for our RPM build - everything in here is something
we no longer have to set up on each build server thanks to Docker:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# This docker image is used on CI for building RPMs
# awscli is installed for uploading to S3 after the build, as well as godep
# and rpm for the rpm build.

FROM centos:6
MAINTAINER StorReduce

# install EPEL repos
RUN yum -y install wget &amp;amp;&amp;amp; \
    wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm &amp;amp;&amp;amp; \
    yum -y install epel-release-6-8.noarch.rpm &amp;amp;&amp;amp; \
    rm epel-release-6-8.noarch.rpm

# install dev packages
RUN yum -y groupinstall &#39;Development Tools&#39;

# install our dependencies
RUN yum -y install which &amp;amp;&amp;amp; \
    yum -y install tar &amp;amp;&amp;amp; \
    yum -y install hg &amp;amp;&amp;amp; \
    yum -y install git &amp;amp;&amp;amp; \
    yum -y install python-pip &amp;amp;&amp;amp; \
    yum -y clean all &amp;amp;&amp;amp; \
    pip install awscli &amp;amp;&amp;amp; \
    mkdir /root/.aws

# install go
RUN wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz &amp;amp;&amp;amp; \
    tar -C /usr/local -xzf go1.4.2.linux-amd64.tar.gz &amp;amp;&amp;amp; \
    rm go1.4.2.linux-amd64.tar.gz

# configure go
RUN mkdir -p /go/src /go/bin &amp;amp;&amp;amp; chmod -R 777 /go
ENV GOPATH /go
ENV PATH /usr/local/go/bin:/go/bin:$PATH
WORKDIR /go

# install godep
RUN go get github.com/tools/godep
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, when the builds runs you get the same great logging output from TeamCity,
here you can see the build executing inside the Docker container:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://tylerpower.io/images/teamcity-with-docker/build-log.png&#34;&gt;&lt;img src=&#34;http://tylerpower.io/images/teamcity-with-docker/build-log.png&#34; alt=&#34;TeamCity Docker Build Log&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Linear and Exponential Back-Off for Go</title>
      <link>http://tylerpower.io/post/go-backoff/</link>
      <pubDate>Sun, 19 Apr 2015 15:25:22 +1200</pubDate>
      
      <guid>http://tylerpower.io/post/go-backoff/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently been working on extending my side project &lt;a href=&#34;https://github.com/Diggs/connectrix&#34;&gt;Connectrix&lt;/a&gt;
to support receiving event data from Twitter. The natural fit was to use the
&lt;a href=&#34;https://dev.twitter.com/streaming/overview&#34;&gt;Twitter Stream API&lt;/a&gt; to receive events
via a push with low latency and low overhead. I looked around and found a couple
of Go libraries for consuming the Twitter Stream, but one of them looked very
hacky and far from idiomatic Go code, while the second was very large and made
a lot of assumptions about the use case. So, I started to write my own low-level,
hopefully idiomatic, library for connecting to these streams, the result is
&lt;a href=&#34;https://github.com/Diggs/go-http-stream-reader&#34;&gt;go-http-stream-reader&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the interesting things about the Twitter Stream API is that is enforces a
rate limit, which needs to be managed by adhering to their published back off
rules - if you don&amp;rsquo;t back off when told to then they will punish you for it by making
you wait even longer.&lt;/p&gt;

&lt;p&gt;From the Twitter Streaming API docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once an established connection drops, attempt to reconnect immediately. If the reconnect fails, slow down your reconnect attempts according to the type of error experienced:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Back off linearly for TCP/IP level network errors&lt;/em&gt;. These problems are generally temporary and tend to clear quickly. Increase the delay in reconnects by 250ms each attempt, up to 16 seconds.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Back off exponentially for HTTP errors&lt;/em&gt; for which reconnecting would be appropriate. Start with a 5 second wait, doubling each attempt, up to 320 seconds.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Back off exponentially for HTTP 420 errors&lt;/em&gt;. Start with a 1 minute wait and double each attempt. Note that every HTTP 420 received increases the time you must wait until rate limiting will no longer will be in effect for your account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To manage these back off rules I needed to implement an exponential back off algorithm.
This algorithm is very trivial but it comes up frequently so I thought it would be
good to create a little Go library to take care of it. The result of that is &lt;a href=&#34;https://github.com/Diggs/go-backoff&#34;&gt;go-backoff&lt;/a&gt;.
It implements linear, exponential and exponential with full jitter back off
algorithms and makes it trivial to use them. For a more detailed look at the use
cases for exponential back off algorithms and their behaviour then take a look at
this great post from the &lt;a href=&#34;http://www.awsarchitectureblog.com/2015/03/backoff.html&#34;&gt;Amazon AWS blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use go-backoff it&amp;rsquo;s as simple as instantiating a backoff instance with the desired behaviour:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;// Back off linearly, starting at 250ms, capping at 16 seconds
linear = backoff.NewLinear(250*time.Millisecond, 16*time.Second)

// Back off exponentially, starting at 5 seconds, capping at 320 seconds
exp = backoff.NewExponential(5*time.Second, 320*time.Second)

// Back off exponentially, starting at 1 minute, with no cap
expt = backoff.NewExponential(time.Minute, 0)

// Back off between 0 and exponentially, starting at 30 seconds, capping at 10 minutes
expj = backoff.NewExponentialFullJitter(30*time.Second, 10*time.Minute)

for {
  err := tryDoThing()
  if err != nil {
    glog.Debugf(&amp;quot;Backing off %d second(s)&amp;quot;, exp.NextDuration/time.Second)
    exp.Backoff()
  } else {
    exp.Reset()
    break
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can find go-backoff on my GitHub here: &lt;a href=&#34;https://github.com/Diggs/go-backoff&#34;&gt;go-backoff&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>