<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>BenSnape.com</title>
    <description>A blog about software development.</description>
    <link>http://bensnape.com</link>
    <atom:link href="http://bensnape.com/feed.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Terraform Design Patterns: the Terrafile</title>
      <description>&lt;p&gt;&lt;em&gt; Special thanks to my colleague &lt;a href=&quot;https://github.com/xiii&quot;&gt;Efstathios Xagoraris&lt;/a&gt;, who laid the original foundations for this concept,
as well as the rest of the team at ITV for their valued input.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Taken straight from the documentation, &lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;provides a common configuration to launch infrastructure — from physical and virtual servers to email and DNS providers. Once launched, Terraform safely and efficiently changes infrastructure as the configuration is evolved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is an example which creates a &lt;a href=&quot;https://aws.amazon.com/vpc/&quot;&gt;VPC&lt;/a&gt; using a &lt;a href=&quot;https://www.terraform.io/intro/getting-started/modules.html&quot;&gt;Terraform module&lt;/a&gt;
(similar to a class in a programming language).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/terraform-community-modules/tf_aws_vpc/?ref=v1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cidr&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;private_subnets&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.1.0/24,10.0.2.0/24,10.0.3.0/24&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;public_subnets&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.0.101.0/24,10.0.102.0/24,10.0.103.0/24&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;azs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;us-west-2a,us-west-2b,us-west-2c&quot;&lt;/span&gt;&lt;span class=&quot;x&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;Straightforward so far.&lt;/p&gt;

&lt;p&gt;However, what immediately struck me (coming from a development background) was the way that modules were referenced - i.e. specifying the module &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; within
the body of the module implementation.&lt;/p&gt;

&lt;p&gt;Combining the module source with its use feels like a mix of concerns to me.&lt;/p&gt;

&lt;p&gt;Additionally, each time you reference a module you must specify its source - even if you used that module elsewhere in your project.&lt;/p&gt;

&lt;p&gt;I believe that abstracting the &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; location to another file (separate from the implementation) would make much more sense.&lt;/p&gt;

&lt;h2 id=&quot;module-versioning&quot;&gt;Module Versioning&lt;/h2&gt;

&lt;p&gt;Before we cover how we might achieve that, let’s quickly cover &lt;a href=&quot;https://www.terraform.io/docs/modules/sources.html#ref&quot;&gt;Terraform module versioning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is also possible (and good practice) to tack on a version at the end of the &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; parameter.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/terraform-community-modules/tf_aws_vpc/?ref=v1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;x&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;Specifying the version (e.g. a git tag) is great as it means multiple teams can contribute to the same Terraform modules without breaking functionality
for others.&lt;/p&gt;

&lt;p&gt;However, upgrading even a single module in a project can be quite a laborious and manual process. Consider a setup with dozens or even
hundreds of ASGs (autoscale groups), spread across numerous &lt;code class=&quot;highlighter-rouge&quot;&gt;.tf&lt;/code&gt; files and various environments (QA, SIT, Stage, Prod etc.) with each
using a Terraform module to implement said ASGs.&lt;/p&gt;

&lt;p&gt;Any non-trivial project will require many other modules e.g. Security Groups, VPCs, subnets, Route53, EBS etc. - suddenly you have
a lot of things to change!&lt;/p&gt;

&lt;h2 id=&quot;terrafile&quot;&gt;Terrafile&lt;/h2&gt;

&lt;p&gt;The combination of &lt;em&gt;a mix of concerns with the module source and implementation&lt;/em&gt; with &lt;em&gt;a potentially laborious and error prone module upgrade process&lt;/em&gt; resulted
in the creation of a &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; to address these issues.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; is simple YAML config that gives you a single, convenient location that lists all your external module dependencies.&lt;/p&gt;

&lt;p&gt;The idea is modelled on similar patterns in other languages - e.g. Ruby with its &lt;code class=&quot;highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; (technically provided by the &lt;code class=&quot;highlighter-rouge&quot;&gt;bundler&lt;/code&gt; gem).&lt;/p&gt;

&lt;p&gt;Here’s what a &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; might look like.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;tf-aws-vpc&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;source&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;git@github.com:ITV/tf-aws-vpc&quot;&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;version&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;v0.1.1&quot;&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;tf-aws-iam&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;source&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;git@github.com:ITV/tf-aws-iam&quot;&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;version&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;v0.1.0&quot;&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;tf-aws-sg&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;source&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;git@github.com:ITV/tf-aws-sg&quot;&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;version&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;v1.3.1&quot;&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;tf-aws-asg&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;source&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;git@github.com:ITV/tf-aws-asg&quot;&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;version&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;v2.0.0&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Below is a simplistic example in Ruby/Rake of how you might implement the &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; pattern. No gems are required (except Rake of course).
Simply place the code in a &lt;code class=&quot;highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt; and execute using &lt;code class=&quot;highlighter-rouge&quot;&gt;rake get_modules&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;yaml&#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;fileutils&#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# You may want to change this.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;modules_path&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;vendor/modules&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# You may want to change this.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;terrafile_path&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;Terrafile&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_terrafile&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terrafile_path&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_file&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terrafile_path&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;[*] Terrafile does not exist&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_modules_directory&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[*] Creating Terraform modules directory at &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;makedirs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete_cached_terraform_modules&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[*] Deleting cached Terraform modules at &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rm_rf&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Fetch the Terraform modules listed in the Terrafile&#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:get_modules&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;terrafile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_terrafile&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;create_modules_directory&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;delete_cached_terraform_modules&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;terrafile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository_details&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository_details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;source&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository_details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;version&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[*] Checking out &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;colorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modules_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modules_path&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;sb&quot;&gt;`git clone -b &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;amp;&amp;gt; /dev/null`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;Implementation is quick and easy. We’ve covered most of it already but let’s recap.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create your &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Implement a way to read your &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; and fetch the required modules (working Ruby example above).&lt;/li&gt;
  &lt;li&gt;Modify all &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; variables in your Terraform project to point to your new cached modules directory (provided by the &lt;code class=&quot;highlighter-rouge&quot;&gt;modules_path&lt;/code&gt; method above)
rather than GitHub e.g.&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;../vendor/modules/tf_aws_vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;x&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;You can read more about Terraform module sources in the &lt;a href=&quot;https://www.terraform.io/docs/modules/sources.html&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;other-considerations-and-limitations&quot;&gt;Other Considerations and Limitations&lt;/h2&gt;

&lt;p&gt;If you need to support multiple different versions of the same module (an incremental upgrade for instance), the Ruby/Rake implementation
above takes the &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; key name into account. For example, the following will be deployed to &lt;code class=&quot;highlighter-rouge&quot;&gt;vendor/modules/vpc-0.0.1&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;vendor/modules/vpc-2.0.0&lt;/code&gt; respectively.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;vpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.0.1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git@github.com:ITV/tf-aws-vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v0.0.1&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2.0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git@github.com:ITV/tf-aws-vpc&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;v2.0.0&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Additionally, the deletion and subsequent fetching of the Terrafile modules is very simplistic. Each time &lt;code class=&quot;highlighter-rouge&quot;&gt;rake get_modules&lt;/code&gt; is executed, all cached
modules are removed and re-fetched.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It feels repetitive and prone to error to keep specifying modules and their version information, especially for larger teams who share
modules. Terraform is rapidly evolving - to keep up you must frequently update your modules.&lt;/p&gt;

&lt;p&gt;Probably my favourite aspect of using a &lt;code class=&quot;highlighter-rouge&quot;&gt;Terrafile&lt;/code&gt; is being to see at a glance exactly which modules and which versions are being used
by a project, just like a &lt;code class=&quot;highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; or a &lt;code class=&quot;highlighter-rouge&quot;&gt;Puppetfile&lt;/code&gt;. Outdated or buggy dependencies are often the root cause of runtime issues and
this shortens the debugging and investigation considerably when things go wrong.&lt;/p&gt;

&lt;p&gt;I’m a huge fan of Terraform and other Hashicorp products (especially Consul and Vagrant). I hope this design pattern helps others to use
Terraform even more productively.&lt;/p&gt;
</description>
      <pubDate>Thu, 14 Jan 2016 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2016/01/14/terraform-design-patterns-the-terrafile/</link>
      <guid isPermaLink="true">http://bensnape.com/2016/01/14/terraform-design-patterns-the-terrafile/</guid>
    </item>
    
    <item>
      <title>Monitoring Microservices via Graphite with Sensu: Part 1</title>
      <description>&lt;p&gt;For the past 2 years I’ve been been part of the team replacing ITV’s legacy systems with microservices hosted in AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://vimeo.com/101280407&quot;&gt;We’ve&lt;/a&gt;
 &lt;a href=&quot;https://speakerdeck.com/caoilte/domain-service-aggregators&quot;&gt;learnt&lt;/a&gt;
 &lt;a href=&quot;https://speakerdeck.com/stefanco/centos-in-a-deployment-pipeline-continuous-delivery-at-itv&quot;&gt;a&lt;/a&gt;
 &lt;a href=&quot;http://www.infoq.com/presentations/itv-kanban-process?utm_campaign=infoq_content&amp;amp;utm_source=infoq&amp;amp;utm_medium=feed&amp;amp;utm_term=global&quot;&gt;lot&lt;/a&gt;
 so far but certainly one of our biggest challenges to date has been implementing effective monitoring.&lt;/p&gt;

&lt;p&gt;Effective monitoring is difficult - especially when put under the same scrutiny as any other code that ends up in
 production i.e. properly thought out, tested, promoted through environements etc. Indeed, previous monitoring efforts
 on historical projects were usually hastily done post-festum as an afterthought, leading to mixed results.&lt;/p&gt;

&lt;p&gt;Like any modern platform we rely heavily on caching (via Varnish). We rely upon Varnish to cache both our metadata API
 (provided by Scala microservices) and static assets. An initial stab at monitoring our backends was done using RSpec
 tests and Sensu
 (&lt;a href=&quot;http://localhost:4000/2014/12/08/infrastructure-testing-with-sensu-and-rspec/&quot;&gt;see previous blog post&lt;/a&gt;).
 We ran a set of simple HTTP-based RSpec tests which queried our backends directly (i.e. not via the cache). This gave
 us a clear answer to “is service X up or down?”. More elaborate business-logic tests were also present in the test
 pack but were in the minority.&lt;/p&gt;

&lt;p&gt;In theory this sounds like a reasonable approach: we knew exactly when and why a backend became unhealthy rather than
 when the cache decided it was (after &lt;em&gt;n&lt;/em&gt; failed probes).&lt;/p&gt;

&lt;p&gt;However, in reality, it’s not uncommon for individual health probes to fail. This can cause problems in
 microservice-based architecture due to the interconnected nature of the individual components: a failed connection
 between two microservices can not only impact the health of the directly dependent service but potentially cause a
 ripple-effect amongst indirectly dependent services.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/monitoring-varnish-backends-via-graphite-with-sensu/microservices_unhealthy.jpeg&quot; alt=&quot;Microservice health ripple-effect&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;em&gt;The microservice health ripple-effect&lt;/em&gt;&lt;/center&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;In our experience, this is often due to networking issues in the cloud, between on-prem and the cloud and when
 interfacing with legacy systems not designed for the modern era. Kyle Kingsbury gives
 &lt;a href=&quot;https://aphyr.com/posts/288-the-network-is-reliable&quot;&gt;an excellent breakdown&lt;/a&gt; of problems that can occur with networks.&lt;/p&gt;

&lt;p&gt;The short term impact was
 &lt;a href=&quot;http://blog.pagerduty.com/2014/03/stop-monitoring-alert-noise/&quot;&gt;noisy and misleading PagerDuty alerts&lt;/a&gt;, which
 reduced confidence in our alerts. Left unchecked, the story of the boy who cried wolf and an uncomfortable postmortem
 for all involved becomes a real possibility.&lt;/p&gt;

&lt;p&gt;Longer-term we were forced to re-evaluate how tightly integrated our microservices were and decide which were mission
 critical and which weren’t.&lt;/p&gt;

&lt;h4 id=&quot;re-think&quot;&gt;Re-think&lt;/h4&gt;

&lt;p&gt;A different solution was clearly needed; one that was more tolerant and realistic. We’ve covered the main problem with
 the previous alerts: frequency - a problem hugely exacerbated by the stateless nature of the running RSpec tests.
 Consequently, our requirements were:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;state (i.e. historical data)&lt;/li&gt;
  &lt;li&gt;tolerance&lt;/li&gt;
  &lt;li&gt;customisation (per backend)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We already exposed Varnish stats (&lt;code class=&quot;highlighter-rouge&quot;&gt;varnishstats&lt;/code&gt; on the commandline) via the excellent
 &lt;a href=&quot;https://github.com/sensu/sensu-community-plugins/blob/master/plugins/varnish/varnish-metrics.rb&quot;&gt;Sensu plugin provided by the community&lt;/a&gt;
 to use in Grafana (a Graphite dashboard).&lt;/p&gt;

&lt;p&gt;Our Graphite data is kept for 3 months so we have no shortage of historical data to play with!&lt;/p&gt;

&lt;p&gt;Utilising &lt;a href=&quot;http://graphite.readthedocs.org/en/latest/render_api.html&quot;&gt;Graphite’s fantastic API&lt;/a&gt;, all we had to do was
 think carefully about the function we provided in the API call and implement some fairly trivial rules for interpreting
 the subsequent response.&lt;/p&gt;

&lt;p&gt;This new approach - whilst more technically challenging - was actually more straightforward. By relying upon Varnish’s
 definition of healthy/unhealthy backends meant that we did not have to duplicate (or keep pace with) complex probe
 logic per backend in the VCL. Ultimately, we don’t immediately care - from an alerting perspective - if a service has
 flickering health. Varnish will serve from grace in the interim and such flickering anomalies can be exposed more
 efficiently in dashboards.&lt;/p&gt;

&lt;h4 id=&quot;unanticipated-issues&quot;&gt;Unanticipated Issues&lt;/h4&gt;

&lt;p&gt;Unfortunately we had a number of obstacles to overcome whilst designing our new Sensu check.&lt;/p&gt;

&lt;p&gt;Firstly, the Varnish stats Sensu plugin is run quite often (every ten seconds) which makes Graphite API calls
 quite large depending on how much historical data you require and how many backends you have.&lt;/p&gt;

&lt;p&gt;Secondly, incomplete data for a certain interval would return &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt; instead of a numerical value. This presented a
 number of edge-cases.&lt;/p&gt;

&lt;p&gt;Thirdly, how to treat load-balanced backends. Should we raise alerts as soon as one becomes unhealthy? Or should we wait
 until a certain percentage do? In the meantime, when and how loudly do we alert for these unhealthy (but non-critical)
 backends?&lt;/p&gt;

&lt;p&gt;Lastly, and perhaps most importantly, AWS instances are frequently destroyed and recreated, resulting in stale metrics.
 These stale metrics are alerted on which causes much confusion.&lt;/p&gt;

&lt;p&gt;Some of these issues were fairly trivial but some are still being ironed out.&lt;/p&gt;

&lt;p&gt;I’m going to cover in more detail what we did in a subsequent blog post. This will include both the Graphite API
 function we used as well as a full breakdown of our custom Sensu check.&lt;/p&gt;
</description>
      <pubDate>Tue, 07 Apr 2015 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2015/04/07/monitoring-microservices-via-graphite-with-sensu-part-1/</link>
      <guid isPermaLink="true">http://bensnape.com/2015/04/07/monitoring-microservices-via-graphite-with-sensu-part-1/</guid>
    </item>
    
    <item>
      <title>Infrastructure Testing with Sensu and RSpec</title>
      <description>&lt;h4 id=&quot;the-divide&quot;&gt;The Divide&lt;/h4&gt;

&lt;p&gt;Despite agile, cross-functional product teams there is often still a clear separation between Engineering (development and testing functions) and Operations.
Assuming a Continuous Delivery pipeline, there is strong collaboration between the three areas in local (e.g. Vagrant), test and stage environments.
However, when it comes to the production environment, Operations typically have the keys to the castle (complete with moat and drawbridge).&lt;/p&gt;

&lt;p&gt;This isn’t necessarily a bad thing - roles and responsibilities are clearly defined for good reason.
But it does help reinforce the Engineering-Operations divide of “we build it, you run it”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/infrastructure-testing-with-sensu-and-rspec/the_divide.jpeg&quot; alt=&quot;The Engineering-Operations Divide&quot; /&gt;&lt;/p&gt;
&lt;center&gt;[1] &lt;em&gt;Metrics by coda hale : to know your app’ health - Izzet Mustafaiev&lt;/em&gt;&lt;/center&gt;

&lt;h4 id=&quot;shared-responsibility&quot;&gt;Shared Responsibility&lt;/h4&gt;

&lt;p&gt;A tangible way to help bridge this divide is with a set of behaviour-driven infrastructure tests.&lt;/p&gt;

&lt;p&gt;The idea is to run a set of tests at regular intervals across all environments (especially Production) to monitor your application from a &lt;em&gt;behavioural&lt;/em&gt; perspective.
When tests fail, they raise very specific errors regarding your application. The benefits here are twofold:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It enables Operations’ to understand the application behaviour.&lt;/li&gt;
  &lt;li&gt;It improves Engineering’s understanding of how the environment effects the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Over time, this leads to increased collaboration between two teams that tend to be deeply entrenched.&lt;/p&gt;

&lt;p&gt;Operations will be exposed to very specific alerts when tests fail which they can feed back to the Engineering team (e.g. service X has gone unhealthy because it depends on service Y which has lost database connectivity).
They can also suggest - and contribute - new tests for missed or problem areas.&lt;/p&gt;

&lt;p&gt;The Engineering team can understand how their application works at scale in a complex environment (e.g. with load-balancing, caching, multiple instances).
This can aid troubleshooting complicated edge-cases such as message queuing, race conditions and cache invalidation which may be difficult to simulate locally.&lt;/p&gt;

&lt;p&gt;The goals of universal logging, graphing, trending and alerting are strong drivers for both teams, helping to expose, debug and fix issues.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/infrastructure-testing-with-sensu-and-rspec/shared_responsibility.jpeg&quot; alt=&quot;Shared Responsibility&quot; /&gt;&lt;/p&gt;
&lt;center&gt;[2] &lt;em&gt;Metrics-Driven Engineering - Mike Brittain&lt;/em&gt;&lt;/center&gt;

&lt;h4 id=&quot;introducing-sensu&quot;&gt;Introducing Sensu&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://sensuapp.org/docs/0.16/overview&quot;&gt;Sensu&lt;/a&gt; is an open-source monitoring framework designed with the cloud in mind. Taken straight from the documentation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sensu takes the results of “check” scripts run across many systems, and if certain conditions are met; passes their information to one or more “handlers”. Checks are used, for example, to determine if a service like Apache is up or down. Checks can also be used to collect data, such as MySQL query statistics or Rails application metrics. Handlers take actions, using result information, such as sending an email, messaging a chat room, or adding a data point to a graph.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From my experience of using Sensu for the past few months across all ITV’s AWS environments (including Production), I can confirm that it’s a really awesome tool.
It’s much more powerful, configurable and easy-to-use than Zabbix or Nagios.&lt;/p&gt;

&lt;p&gt;Sensu provides an &lt;a href=&quot;https://github.com/sensu/sensu-community-plugins&quot;&gt;open-source repository&lt;/a&gt; which contains a large number of “checks” and “metrics” which you can use. You can also use any existing &lt;a href=&quot;http://www.nagios.org/&quot;&gt;Nagios plugins&lt;/a&gt; that you might have.&lt;/p&gt;

&lt;p&gt;You can fully configure Sensu using &lt;a href=&quot;https://github.com/sensu/sensu-puppet&quot;&gt;Puppet&lt;/a&gt; or &lt;a href=&quot;https://github.com/sensu/sensu-chef&quot;&gt;Chef&lt;/a&gt; (we use the former).&lt;/p&gt;

&lt;p&gt;It also provides an API which also allows you to configure different dashboards e.g. &lt;a href=&quot;https://github.com/sensu/uchiwa&quot;&gt;Uchiwa&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;a-working-example&quot;&gt;A Working Example&lt;/h4&gt;
&lt;p&gt;Let’s take a look at how you can run your infrastructure tests and raise alerts for failures.&lt;/p&gt;

&lt;p&gt;For this we need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;some tests to run&lt;/li&gt;
  &lt;li&gt;a Sensu server&lt;/li&gt;
  &lt;li&gt;a Sensu client&lt;/li&gt;
  &lt;li&gt;a Sensu “check” to run our tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately, &lt;a href=&quot;https://github.com/sensu/sensu-community-plugins/blob/master/plugins/rspec/check-rspec.rb&quot;&gt;I already wrote an RSpec Sensu check plugin!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a &lt;a href=&quot;https://github.com/bsnape/sensu-rspec-integration&quot;&gt;working example&lt;/a&gt; that contains all the above where you can see Sensu running some failing RSpec tests and consequently raising alerts.&lt;/p&gt;

&lt;p&gt;Once you have the Vagrant VMs up and running you can see the (deprecated) Sensu dashboard with the alerts:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/infrastructure-testing-with-sensu-and-rspec/sensu_dashboard.jpeg&quot; alt=&quot;Sensu Dashboard&quot; /&gt;&lt;/p&gt;

&lt;h5 id=&quot;credits&quot;&gt;Credits&lt;/h5&gt;

&lt;p&gt;Thanks to Michael Richardson[3] for originally coming up with a similar idea (Sensu + Serverspec).&lt;/p&gt;

&lt;p&gt;Thanks to Graham Taylor[4] for the Sensu Serverspec plugin which I based my Sensu Rspec plugin on.&lt;/p&gt;

&lt;p&gt;Thanks to Rob Taylor[5] for replicating the first two images ([1] and [2] respectively).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;[1] &lt;a href=&quot;http://www.slideshare.net/IzzetMustafaiev/metrics-by-coda-hale&quot;&gt;Metrics by coda hale : to know your app’ health - Izzet Mustafaiev&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[2] &lt;a href=&quot;http://www.slideshare.net/mikebrittain/metricsdriven-engineering&quot;&gt;Metrics-Driven Engineering - Mike Brittain&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[3] &lt;a href=&quot;http://www.slideshare.net/m_richardson/serverspec-and-sensu-testing-and-monitoring-collide&quot;&gt;Serverspec and Sensu - Testing and Monitoring collide - Michael Richardson&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[4] &lt;a href=&quot;https://github.com/sensu/sensu-community-plugins/commit/2f4c665be2c56983d4df85981f5e89f389d0083f&quot;&gt;Serverspec Sensu plugin - Graham Taylor (@tayzlor)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;[5] &lt;a href=&quot;http://www.rctaylor.co.uk/&quot;&gt;Rob Taylor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Mon, 08 Dec 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/12/08/infrastructure-testing-with-sensu-and-rspec/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/12/08/infrastructure-testing-with-sensu-and-rspec/</guid>
    </item>
    
    <item>
      <title>Using Ruby Metaprogramming to Output Timestamped Kibana Logs in Your RSpec Tests</title>
      <description>&lt;p&gt;At ITV, we verify the behaviour of our Scala micro-service API using core Ruby and RSpec. In order to 
&lt;a href=&quot;http://puppetlabs.com/blog/continuous-delivery-vs-continuous-deployment-whats-diff&quot;&gt;Continuously Deliver&lt;/a&gt; changes to
each micro-service, we test individual commits (forming a “candidate service”) in isolation against its proven stable
counterparts (from prior runs of this very process).&lt;/p&gt;

&lt;p&gt;Consequently, test failures can usually be narrowed down to a failure in an individual micro-service or in the contract
between two or more micro-services.&lt;/p&gt;

&lt;p&gt;As we use &lt;a href=&quot;http://logstash.net/&quot;&gt;Logstash&lt;/a&gt;/&lt;a href=&quot;http://www.elasticsearch.org/overview/kibana/&quot;&gt;Kibana&lt;/a&gt; across our
environments, we can supplement test failures with a holistic view of the system when those failures occurred.&lt;/p&gt;

&lt;p&gt;Of course we only want to see the Kibana logs that encompasses the failed test (and all relevant hooks), otherwise we’ll
just be dealing with noise.&lt;/p&gt;

&lt;h4 id=&quot;rspec-hooks&quot;&gt;RSpec Hooks&lt;/h4&gt;

&lt;p&gt;In order to solve our problem, we need to do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Initialise a variable with the time before the entire test suite begins.&lt;/li&gt;
  &lt;li&gt;Update the variable &lt;em&gt;after&lt;/em&gt; each individual test (so that all &lt;code class=&quot;highlighter-rouge&quot;&gt;before&lt;/code&gt; test hooks are taken into account).&lt;/li&gt;
  &lt;li&gt;Use the variable defined in (1) if it’s the very first test that fails or (2) if it’s for any test afterwards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, the &lt;code class=&quot;highlighter-rouge&quot;&gt;before(:suite)&lt;/code&gt; hook - which, as the name implies, runs once before your entire suite of tests -
does not allow instance variables defined in that scope to be used in your tests.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:suite&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;vi&quot;&gt;@my_instance_variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;foo&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all&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;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@my_instance_variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;be_truthy&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Results in:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Unable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matching&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backtrace&lt;/span&gt;
       &lt;span class=&quot;ss&quot;&gt;expected: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;truthy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;got: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;How about a
&lt;a href=&quot;http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Class_Variables&quot;&gt;class variable&lt;/a&gt; instead?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:suite&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;vc&quot;&gt;@@my_class_variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;bar&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all&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;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vc&quot;&gt;@@my_class_variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;be_truthy&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This now works but will result in Ruby runtime warnings every time the class variable is accessed:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;/Users/bensnape/git/CPS/spec/helpers/unit_spec_helper.rb:27: warning: class variable access from toplevel&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So not the ideal solution.&lt;/p&gt;

&lt;h4 id=&quot;enter-classvariableset&quot;&gt;Enter class_variable_set&lt;/h4&gt;

&lt;p&gt;Instead, using Ruby metaprogramming and class_variable_set we can modify a variable that holds a timestamp in a class
that hasn’t been created yet.&lt;/p&gt;

&lt;p&gt;When we do need to create an instance of this class (i.e. when we get a test failure), it will be initialised with the
timestamp from the last &lt;code class=&quot;highlighter-rouge&quot;&gt;after&lt;/code&gt; hook (or from the intial &lt;code class=&quot;highlighter-rouge&quot;&gt;before(:suite)&lt;/code&gt; hook if the very first test fails):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GlobalDateTime&lt;/span&gt;
  &lt;span class=&quot;vc&quot;&gt;@@start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;start_time&lt;/span&gt;
    &lt;span class=&quot;vc&quot;&gt;@@start_time&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:suite&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;no&quot;&gt;GlobalDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_variable_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:@@start_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;%Y-%m-%d %H:%M:%S&#39;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;kibana_url&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;http://logstash.cpp.o.itv.net.uk/kibana/#/dashboard/elasticsearch/All%20Microservices%20By%20Date?&#39;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;times&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;ss&quot;&gt;from: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GlobalDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
          &lt;span class=&quot;ss&quot;&gt;to:   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&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;n&quot;&gt;kibana_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&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;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&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;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&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;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;amp;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Check the server logs at: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kibana_url&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;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GlobalDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_variable_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:@@start_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alternatively, you can change &lt;code class=&quot;highlighter-rouge&quot;&gt;start_time&lt;/code&gt; to be a class method instead:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start_time&lt;/span&gt;
  &lt;span class=&quot;vc&quot;&gt;@@start_time&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which means that you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;start_time&lt;/code&gt; directly on the &lt;code class=&quot;highlighter-rouge&quot;&gt;GlobalDateTime&lt;/code&gt; class, rather than creating a new instance
first:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GlobalDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_format&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;nf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
      <pubDate>Tue, 26 Aug 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/08/26/using-ruby-metaprogramming-to-output-timestamped-kibana-logs-in-your-rspec-tests/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/08/26/using-ruby-metaprogramming-to-output-timestamped-kibana-logs-in-your-rspec-tests/</guid>
    </item>
    
    <item>
      <title>Introducing Eir - a URL status checker</title>
      <description>&lt;p&gt;In software development, I often find that the simplest tools are the most valuable. If you’ve worked with RabbitMQ
before you’ll know what I mean (joke, Rabbit lovers).&lt;/p&gt;

&lt;p&gt;I’ve been working on implementing a &lt;a href=&quot;http://martinfowler.com/articles/microservices.html&quot;&gt;microservice-based&lt;/a&gt; API at
&lt;a href=&quot;http://www.itv.com&quot;&gt;ITV&lt;/a&gt; for around 18 months now. It’s been an interesting departure from the typically monolithic
systems I’ve worked with in the past. However, despite numerous benefits, microservices offer their own unique
challenges when it comes to testing the system as a whole.&lt;/p&gt;

&lt;p&gt;Interactions and dependencies between each individual - or group of - microservices can be tricky to negotiate. Even
with thorough knowledge of the underlying domain, system-wide acceptance test failures could be due to any number of
reasons and can be tiresome to debug.&lt;/p&gt;

&lt;p&gt;In such cases, the best approach is to methodically debug the system, gradually becoming more hands-on. For example,
tailing application logs, reading exceptions (if any) and inspecting access logs should come after more fundamental
checks such as are all the expected services even running and healthy?&lt;/p&gt;

&lt;p&gt;With this in mind, I wanted a simple, no-frills, graphical URL status checker.&lt;/p&gt;

&lt;p&gt;It turned out that no such thing existed(at least I couldn’t find anything) so I created one.&lt;/p&gt;

&lt;h4 id=&quot;initial-development&quot;&gt;Initial Development&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Eir&quot;&gt;Eir&lt;/a&gt;, in Norse mythology is a goddess of health which I thought was appropriate.&lt;/p&gt;

&lt;p&gt;I sat on my idea of an easy-to-use status checker for some time as I wasn’t quite sure how I was going to architect it.&lt;/p&gt;

&lt;p&gt;I’ve used &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; extensively for creating standalone system mocks and eventually I
realised that the answer had been staring me in the face all along.&lt;/p&gt;

&lt;p&gt;I found a great &lt;code class=&quot;highlighter-rouge&quot;&gt;Sinatra&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;AJAX&lt;/code&gt; &lt;a href=&quot;http://screencasts.org/episodes/ajax-website-with-sinatra-jquery&quot;&gt;screencast&lt;/a&gt;
which helped point me in the right direction.&lt;/p&gt;

&lt;p&gt;Thus, &lt;a href=&quot;http://github.com/bsnape/eir&quot;&gt;Eir&lt;/a&gt; was born.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/bsnape/eir/master/dashboard.png&quot; alt=&quot;Eir Dashboard&quot; title=&quot;Eir Dashboard&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;using-it&quot;&gt;Using it&lt;/h4&gt;

&lt;p&gt;Using it couldn’t get any simpler.&lt;/p&gt;

&lt;p&gt;If you have a Ruby project, add &lt;code class=&quot;highlighter-rouge&quot;&gt;gem &#39;eir&#39;&lt;/code&gt; to your &lt;code class=&quot;highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;. Otherwise, run &lt;code class=&quot;highlighter-rouge&quot;&gt;gem install eir&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code class=&quot;highlighter-rouge&quot;&gt;uris.yaml&lt;/code&gt; with a list of endpoints and their display/short names like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://www.google.co.uk&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Google&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://www.yahoo.co.uk&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Yahoo&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://www.itv.com&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ITV&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then simply type &lt;code class=&quot;highlighter-rouge&quot;&gt;eir&lt;/code&gt; in your terminal and navigate to &lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:8700&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s it, but there are more detailed instructions in &lt;a href=&quot;https://github.com/bsnape/eir&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;looking-ahead&quot;&gt;Looking Ahead&lt;/h4&gt;

&lt;p&gt;I’ve had great feedback from my team so far but &lt;a href=&quot;https://github.com/bsnape/eir/issues/1&quot;&gt;there’s loads more I could do&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I want to hear from more people about how they’d like to use Eir, so feel free to create feature requests.&lt;/p&gt;
</description>
      <pubDate>Wed, 13 Aug 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/08/13/introducing-eir-a-url-status-checker/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/08/13/introducing-eir-a-url-status-checker/</guid>
    </item>
    
    <item>
      <title>Defining Custom Matchers in RSpec</title>
      <description>&lt;p&gt;Defining custom RSpec matchers is really easy and a great way to increase the readability of your tests.&lt;/p&gt;

&lt;p&gt;Using your own matcher is a much better option rather than trying to retrofit RSpec’s built-in matchers to fit your
 individual use case.&lt;/p&gt;

&lt;h4 id=&quot;a-retro-fitted-example&quot;&gt;A retro-fitted example&lt;/h4&gt;

&lt;p&gt;You may want to check that a HTTP response has a particular status code. Using RSpec’s built-in matchers would look 
something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;should return a 200 status code&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# we need the block at the end to prevent non-200 status codes being raised as an error automatically&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RestClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;http://bensnape.com/missing-page&#39;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or response.code.should == 200&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But it would read much better with a custom &lt;code class=&quot;highlighter-rouge&quot;&gt;have_status_code&lt;/code&gt; matcher.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;should return a 200 status code&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RestClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;http://bensnape.com/missing-page&#39;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_status_code&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or response.should have_status_code 200&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;defining-a-custom-matcher&quot;&gt;Defining a custom matcher&lt;/h4&gt;

&lt;p&gt;It’s really easy to do this.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;rspec/expectations&#39;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Matchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:have_status_code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ultimately, all we’re really checking is that the status code of a HTTP request returns a certain value.&lt;/p&gt;

&lt;h4 id=&quot;providing-a-custom-error-message&quot;&gt;Providing a custom error message&lt;/h4&gt;

&lt;p&gt;We can improve our matcher further with a custom exception message. This is where the usefulness of writing your own
 matcher really comes out, as it provides an exact, bespoke error message rather than something generic like 
 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;expected false but got true&quot;&lt;/code&gt; which we’ve all experienced at some point.&lt;/p&gt;

&lt;p&gt;Simply extend the matcher above with a &lt;code class=&quot;highlighter-rouge&quot;&gt;failure_message&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;rspec/expectations&#39;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Matchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:have_status_code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;failure_message&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;expected that &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; would have a status code of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; instead&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When this fails, the error looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_status_code&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or response.should have_status_code 200&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entire&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;here!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;would&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;but&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;got&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instead&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which is useful as it adds more context to the test failure.&lt;/p&gt;

&lt;h4 id=&quot;extending-our-custom-matcher-further&quot;&gt;Extending our custom matcher further&lt;/h4&gt;

&lt;p&gt;Our custom matcher does the job but there are some potential problems with it.&lt;/p&gt;

&lt;p&gt;Perhaps you are using more than one HTTP framework in your tests or - more likely - you are using the 
&lt;a href=&quot;http://www.sinatrarb.com/testing.html&quot;&gt;Rack::Test&lt;/a&gt; framework for unit-testing Sinatra apps as well as an HTTP framework
 such as &lt;code class=&quot;highlighter-rouge&quot;&gt;RestClient&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Curb&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTParty&lt;/code&gt; for integration or acceptance tests for example.&lt;/p&gt;

&lt;p&gt;In such cases it would be a good idea to use the same custom matcher defined above for all cases 
(&lt;a href=&quot;http://en.wikipedia.org/wiki/Don&#39;t_repeat_yourself&quot;&gt;DRY&lt;/a&gt;). However, the APIs can differ e.g. to return the status 
code, &lt;code class=&quot;highlighter-rouge&quot;&gt;RestClient&lt;/code&gt; uses &lt;code class=&quot;highlighter-rouge&quot;&gt;.code&lt;/code&gt; whereas &lt;code class=&quot;highlighter-rouge&quot;&gt;Rack::Test&lt;/code&gt; uses &lt;code class=&quot;highlighter-rouge&quot;&gt;.status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s harness the power of Ruby’s metaprogramming using &lt;code class=&quot;highlighter-rouge&quot;&gt;respond_to?&lt;/code&gt; to handle this.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Matchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:have_status_code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:code&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# RestClient&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Rack::Test&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Of course, the more HTTP frameworks you have the more complexity is introduced.&lt;/p&gt;

&lt;p&gt;It is probably a good idea to tighten up that &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; statement with an &lt;code class=&quot;highlighter-rouge&quot;&gt;elsif&lt;/code&gt; for &lt;code class=&quot;highlighter-rouge&quot;&gt;Rack::Test&lt;/code&gt; and a catch-all &lt;code class=&quot;highlighter-rouge&quot;&gt;else&lt;/code&gt; 
that raises an &lt;code class=&quot;highlighter-rouge&quot;&gt;UnsupportedHTTPFrameworkException&lt;/code&gt; or similar.&lt;/p&gt;

&lt;p&gt;Let’s finish up with our new &lt;code class=&quot;highlighter-rouge&quot;&gt;failure_message&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Matchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:have_status_code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# define this here for visibility in the failure_message scope&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:code&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;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;failure_message&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;expected that &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; would have a status code of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; instead&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To provide a single error message we needed to introduce the &lt;code class=&quot;highlighter-rouge&quot;&gt;status_code&lt;/code&gt; variable and ensure it was at a scope that 
made it available to the &lt;code class=&quot;highlighter-rouge&quot;&gt;failure_message&lt;/code&gt; block. This gave us the opportunity to use the much terser ternary operator 
and split out the fetching of the status code from the matcher comparison.&lt;/p&gt;

&lt;p&gt;May your tests now be more readable…&lt;/p&gt;
</description>
      <pubDate>Mon, 04 Aug 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/08/04/defining-custom-matchers-in-rspec/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/08/04/defining-custom-matchers-in-rspec/</guid>
    </item>
    
    <item>
      <title>RSpec After Failure Hook</title>
      <description>&lt;p&gt;Inevitably, tests fail.&lt;/p&gt;

&lt;p&gt;For tests that run in complex applications and/or complex environments, the test failure alone often does not provide 
enough information to conclusively diagnose the underlying issue.&lt;/p&gt;

&lt;p&gt;Therefore, it’s handy to be able to intercept failed tests and provide extra debug information.&lt;/p&gt;

&lt;p&gt;There are a couple of ways you can configure the after hook, depending on your use-case.&lt;/p&gt;

&lt;h3 id=&quot;generic-after-failure-hook&quot;&gt;Generic after failure hook&lt;/h3&gt;

&lt;p&gt;This is an easy way to provide default debug output for every test failure (e.g. a snapshot of the current state of the 
application/environment, timestamps etc.).&lt;/p&gt;

&lt;h4 id=&quot;rspec-3&quot;&gt;RSpec 3:&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Rspec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# do something&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;rspec-2&quot;&gt;RSpec 2:&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Rspec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# do something&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;specific-after-failure-hook&quot;&gt;Specific after failure hook&lt;/h3&gt;

&lt;p&gt;There are bound to be certain special-cases in your tests which are especially tricky to debug. In situations like this 
you can simply specify an example-level &lt;code class=&quot;highlighter-rouge&quot;&gt;after&lt;/code&gt; hook in your &lt;code class=&quot;highlighter-rouge&quot;&gt;spec&lt;/code&gt; file:&lt;/p&gt;

&lt;h4 id=&quot;rspec-3-1&quot;&gt;RSpec 3:&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# do something&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;rspec-2-1&quot;&gt;RSpec 2:&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# do something&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now go break things!&lt;/p&gt;
</description>
      <pubDate>Fri, 01 Aug 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/08/01/rspec-after-failure-hook/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/08/01/rspec-after-failure-hook/</guid>
    </item>
    
    <item>
      <title>Coloured Output in Jenkins</title>
      <description>&lt;p&gt;Everyone knows that looking at the Jenkins logs to figure out why a build failed can be tedious work.&lt;/p&gt;

&lt;p&gt;You’re usually presented with a wall of text which you need to wade through.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/1.jpeg&quot; alt=&quot;Horrible Jenkins log&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Builds that look like this are difficult for members in a cross-functional team to investigate,
which ultimately slows everyone down.&lt;/p&gt;

&lt;p&gt;Fortunately there are number of improvements we can make for both Ruby and other languages.&lt;/p&gt;

&lt;h3 id=&quot;language-agnostic-colours&quot;&gt;Language-agnostic colours&lt;/h3&gt;

&lt;p&gt;The first step is to install the &lt;a href=&quot;https://wiki.jenkins-ci
.org/display/JENKINS/AnsiColor+Plugin&quot;&gt;Jenkins AnsiColor plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you’ve done that, make sure the xterm colours are present in the Jenkins configuration screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/2.jpeg&quot; alt=&quot;AnsiColor Setup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You have to enable colours for each job that you want to see colours for.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/3.jpeg&quot; alt=&quot;AnsiColor job&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You should now be running with basic colour output. Great.&lt;/p&gt;

&lt;h3 id=&quot;rspec-colours&quot;&gt;RSpec colours&lt;/h3&gt;

&lt;p&gt;To get RSpec colours working in Jenkins you have to specify the following in the RSpec configuration block:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tty&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is because the Jenkins shell is a pseudo TTY.&lt;/p&gt;

&lt;p&gt;Looking good.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/4.jpeg&quot; alt=&quot;Rspec Colours&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;ruby-colours-colorize-gem&quot;&gt;Ruby colours (colorize gem)&lt;/h3&gt;

&lt;p&gt;If you want to get some really sweet colour action going on like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/5.jpeg&quot; alt=&quot;Ruby colours&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You have to do a couple of things.&lt;/p&gt;

&lt;p&gt;First, add the &lt;code class=&quot;highlighter-rouge&quot;&gt;colorize&lt;/code&gt; gem to your Gemfile.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;https://rubygems.org&#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;colorize&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next, you need to monkey-patch the Ruby &lt;code class=&quot;highlighter-rouge&quot;&gt;IO&lt;/code&gt; class (yeah, I know). There’s some discussion on this
&lt;a href=&quot;https://github.com/dblock/jenkins-ansicolor-plugin/issues/5&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IO&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isatty&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you just need to use &lt;code class=&quot;highlighter-rouge&quot;&gt;colorize&lt;/code&gt; in your Ruby code…&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello there&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;colorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;picking-the-colours-to-use&quot;&gt;Picking the colours to use&lt;/h3&gt;

&lt;p&gt;Here’s a really handy tip if you want to experiment with which colours work best for you.&lt;/p&gt;

&lt;p&gt;Create a Jenkins job with the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ruby -r rubygems -e &lt;span class=&quot;s2&quot;&gt;&quot;class IO; def isatty; true; end; end;&quot;&lt;/span&gt; -e &lt;span class=&quot;s2&quot;&gt;&quot;require &#39;colorize&#39;&quot;&lt;/span&gt; -e &lt;span class=&quot;s2&quot;&gt;&quot;String.color_matrix(&#39;FOO&#39;)&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now run it…et voila!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/6.jpeg&quot; alt=&quot;Colorize matrix&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you’re feeling really helpful you can provide your team members with a glossary of what each colour means
in your build:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://bensnape.com/images/jenkins-coloured-output/7.jpeg&quot; alt=&quot;Colour glossary&quot; /&gt;&lt;/p&gt;
</description>
      <pubDate>Sun, 23 Mar 2014 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2014/03/23/coloured-output-in-jenkins/</link>
      <guid isPermaLink="true">http://bensnape.com/2014/03/23/coloured-output-in-jenkins/</guid>
    </item>
    
    <item>
      <title>Managing Multiple Private SSH Keys</title>
      <description>&lt;p&gt;Until recently, I wasn’t really aware of the concept of having multiple private/public SSH key pairs in order to access different systems. As a developer, I’ve obviously used SSH fairly frequently over a number of years but it’s been more to &lt;em&gt;get things done&lt;/em&gt;, e.g. setting up my GitHub account or connecting to some server at work once in a blue moon.&lt;/p&gt;

&lt;p&gt;Typically, I would have a single pair of keys which I would use in all cases. So, my public key would be held by GitHub, my Mac server, my &lt;a href=&quot;https://code.google.com/p/gerrit/&quot; title=&quot;Gerrit&quot; target=&quot;_blank&quot;&gt;Gerrit&lt;/a&gt; server, Jenkins, TeamCity and so on. If I lost my private key it wouldn’t be a terrible loss - most of the systems I use are only accessible on the company intranet and are easily changed. I now know this is not the most secure setup (hey, I’m no sysadmin) but I also know that I’m not the only person doing this!&lt;/p&gt;

&lt;p&gt;So what happens when we want to SSH onto a machine using a different key pair?&lt;/p&gt;

&lt;h3 id=&quot;manually-managing-multiple-private-keys&quot;&gt;Manually managing multiple private keys&lt;/h3&gt;

&lt;p&gt;Let’s assume you’ve &lt;a href=&quot;https://help.github.com/articles/generating-ssh-keys&quot; title=&quot;ssh-keygen&quot; target=&quot;_blank&quot;&gt;already set up new key pairs&lt;/a&gt; in your &lt;code&gt;~/.ssh&lt;/code&gt; directory. If you &lt;code&gt;ls&lt;/code&gt; in that directory, you might see something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;ls ~/.ssh/

jenkins  jenkins.pub  github  github.pub  known_hosts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When SSH’ing onto a server - e.g. Jenkins - you would usually just type &lt;code&gt;$ ssh user@host&lt;/code&gt; and be done with it (assuming you have the default &lt;code&gt;id_rsa&lt;/code&gt; and &lt;code&gt;id_rsa.pub&lt;/code&gt; keys in your &lt;code&gt;~/.ssh/&lt;/code&gt; folder).&lt;/p&gt;

&lt;p&gt;Because we now have separate keys for each server, we must specify the location of the corresponding private key (in your &lt;code&gt;~/.ssh/&lt;/code&gt; directory) when we attempt to SSH onto the server with the &lt;code&gt;-i&lt;/code&gt; flag:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;ssh -i ~/.ssh/jenkins ben.snape@jenkins.itv.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This does the trick but it’s very… &lt;strong&gt;wordy&lt;/strong&gt;. Surely we can shorten it?&lt;/p&gt;

&lt;h3 id=&quot;introducing-ssh-config&quot;&gt;Introducing SSH config&lt;/h3&gt;

&lt;p&gt;Fortunately, there is a simple way to do this.&lt;/p&gt;

&lt;p&gt;We can define a &lt;code&gt;config&lt;/code&gt; in our SSH directory. The format looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Host           short-name
HostName       some.very.long.server.name
IdentityFile   ~/.ssh/private_ssh_key
User           username-on-remote-server&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can specify as many hosts as you like. Also, you may not need the &lt;code&gt;User&lt;/code&gt; field depending on your use-case.&lt;/p&gt;

&lt;p&gt;Let’s fill this out with my example Jenkins login from above and test it out:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Host           jenkins
HostName       jenkins.itv.com
IdentityFile   ~/.ssh/jenkins
User           ben.snape&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we can really easily connect by typing:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;ssh jenkins&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Much better!&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Apr 2013 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2013/04/30/managing-multiple-private-ssh-keys/</link>
      <guid isPermaLink="true">http://bensnape.com/2013/04/30/managing-multiple-private-ssh-keys/</guid>
    </item>
    
    <item>
      <title>If-Less Programming</title>
      <description>&lt;p&gt;While reading &lt;a href=&quot;http://news.ycombinator.com/&quot; title=&quot;Hacker News&quot; target=&quot;_blank&quot;&gt;Hacker News&lt;/a&gt; I came across a very interesting &lt;a href=&quot;http://alisnic.github.com/posts/ifless/&quot; title=&quot;If-less programming&quot; target=&quot;_blank&quot;&gt;blog post&lt;/a&gt; which contained a small discussion based on a &lt;a href=&quot;http://www.youtube.com/watch?v=4F72VULWFvc&amp;amp;feature=channel&quot; title=&quot;The Clean Code Talks -- Inheritance, Polymorphism, &amp;amp; Testing&quot; target=&quot;_blank&quot;&gt;Tech Talk by Google&lt;/a&gt; about how and why you should avoid &lt;code&gt;if&lt;/code&gt; statements and &lt;code&gt;switches&lt;/code&gt; through polymorphism.&lt;/p&gt;

&lt;p&gt;The basic premise is that if you are repeatedly checking if an object is a type of something or if some flag is set then you’re probably violating one of the fundamental concepts of Object-Oriented Programming. Rather than introducing (complicated and/or cryptic) logic for a generic and &lt;a href=&quot;http://en.wikipedia.org/wiki/Cohesion_(computer_science)&quot; title=&quot;Cohesion&quot; target=&quot;_blank&quot;&gt;incohesive&lt;/a&gt; class, it is advisable to implement a &lt;a href=&quot;http://en.wikipedia.org/wiki/Open/closed_principle&quot; title=&quot;Open/Closed Principle&quot; target=&quot;_blank&quot;&gt;concrete subclass of an abstract superclass&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ultimately, you should be trying to implement each &lt;a href=&quot;http://en.wikipedia.org/wiki/Solid_(object-oriented_design)&quot; title=&quot;SOLID&quot; target=&quot;_blank&quot;&gt;fundamental OOP concept (SOLID)&lt;/a&gt; - &lt;em&gt;pragmatically of course&lt;/em&gt;, as over-engineering a solution is just as poor practise.&lt;/p&gt;

&lt;h3 id=&quot;why-should-we-do-this&quot;&gt;Why Should We Do This?&lt;/h3&gt;

&lt;p&gt;The main benefits (taken from the Tech Talk slides) are threefold:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Functions without ifs are easier to read&lt;/li&gt;
  &lt;li&gt;Functions without ifs are easier to test&lt;/li&gt;
  &lt;li&gt;Polymorphic systems are easier to maintain (and extend) - again, refer to the &lt;a href=&quot;http://en.wikipedia .org/wiki/Open/closed_principle&quot; title=&quot;Open/Closed Principle&quot; target=&quot;_blank&quot;&gt;Open/Closed Principle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;an-example&quot;&gt;An Example&lt;/h3&gt;

&lt;p&gt;I’m going to adapt a non-production code (i.e. test code) refactor I recently worked on.&lt;/p&gt;

&lt;p&gt;My main responsibility is to look after ITV’s (exposed) back-end systems, including &lt;strong&gt;Mercury&lt;/strong&gt;, our video content playlist service. Given a production ID and the client platform (e.g. DotCom for ITVPlayer.com, Android for the ITV Android app etc.) then Mercury returns the requested piece of content. The platform part is important as Mercury handles many platform-specific things such as lower content bitrates for the mobile platforms and higher bitrates for YouView and the ITVPlayer site, for example.&lt;/p&gt;

&lt;p&gt;So of course, it is necessary to test that platform-specific things work on the intended platforms only.&lt;/p&gt;

&lt;h3 id=&quot;the-delwrong-waydel-fast-way-to-complexity-and-technical-debt&quot;&gt;The &lt;del&gt;wrong way&lt;/del&gt; fast way to complexity and technical debt&lt;/h3&gt;

&lt;p&gt;Here’s a basic scenario that I’ve already given some background on above:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;no&quot;&gt;Scenario&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Platform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitrates&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mercury&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playlist&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;correct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitrates&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Examples&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;n&quot;&gt;platform&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DotCom&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Android&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mobile&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Samsung&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PS3&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;YouView&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;Pretty straightforward. Now we can implement these steps quite naively:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I request the Mercury playlist for (\w+)$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@savon_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@mercury&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_client&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;production&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@production_helpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_production_from_config&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@mercury&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;playlist_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@savon_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I get the correct bitrates for (.*)$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;found_bitrates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;//VideoEntries/Video/MediaFiles/MediaFile&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&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;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;bitrate&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;expected_bitrates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/android/i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;150000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;800000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1200000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/samsung/i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1200000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/youview/i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1200000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/ps3/i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;800000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/mobile/i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;800000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1200000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;found_bitrates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected_bitrates&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I think it’s implementations like this that give tools like Cucumber and its proponents a bad name. The step implementations are littered with conditional logic, unnecessary passing through of variables to various classes and a significant number of instance variables (compared to local variables).&lt;/p&gt;

&lt;h3 id=&quot;refactoring-and-removing-the-conditional-logic&quot;&gt;Refactoring and removing the conditional logic&lt;/h3&gt;

&lt;p&gt;A much better approach is to properly model your domain (yes, even with testing).&lt;/p&gt;

&lt;p&gt;Platforms are objects and should be treated as such. Mercury is also an object but it should be part of platform objects in a &lt;em&gt;has-a&lt;/em&gt; relationship.&lt;/p&gt;

&lt;p&gt;Let’s refactor our code from above starting with the Cucumber feature:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;no&quot;&gt;Scenario&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Platform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitrates&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;piece&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;catchup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mercury&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playlist&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;correct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitrates&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Examples&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;n&quot;&gt;platform&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DotCom&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Android&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mobile&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Samsung&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PS3&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;YouView&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;The plan is to have a data (platform object) instantiation pre-condition in the &lt;em&gt;Given&lt;/em&gt; step, before having generic &lt;em&gt;When&lt;/em&gt; and &lt;em&gt;Then&lt;/em&gt; steps which will harness our new object-oriented design.&lt;/p&gt;

&lt;p&gt;The new steps can be simply re-written (note the meta-programming in the &lt;em&gt;Given&lt;/em&gt; step):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I have a piece of (\w+) catchup content$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@platform&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;const_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camelcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I request the Mercury playlist$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request_playlist&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I get the correct bitrates$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;playlist_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bitrates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bitrates&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we need our platform base class. The idea here is to define generic platform behaviour which the individual subclasses can override if required.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Platform&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:playlist_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:playlist_response&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:production&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@production&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;no&quot;&gt;EnvConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;generic_production&#39;&lt;/span&gt;&lt;span class=&quot;p&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;vi&quot;&gt;@playlist_request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mercury&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@playlist_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mercury&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@playlist_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ProductionId&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;vi&quot;&gt;@production&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;request_playlist&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@playlist_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@playlist_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;An example platform might look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Youview&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Platform&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:bitrates&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@bitrates&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;mi&quot;&gt;1200000&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;nf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;request_playlist&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@playlist_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:siteInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Platform&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;s1&quot;&gt;&#39;YouView&#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As a result of this new design, it is so easy to see the generic and specific behaviour for each platform. Not only that, but the test code itself is much easier to write, maintain and read. I’ve no doubt that any developer could come in and quickly get up and running.&lt;/p&gt;

&lt;p&gt;I’ve deliberately left out the Mercury classes as they could contain some commercially sensitive information (especially the stuff around adverts). With that aside, the Mercury response class was a really important refactor as it encapsulates all the tricky xpaths and regular expression parsing of the SOAP response in one place. Again, for any platform-specific behaviour it was just a case of creating a concrete subclass of &lt;code&gt;Mercury::Response&lt;/code&gt; to implement the differences.&lt;/p&gt;

&lt;h3 id=&quot;staying-out-of-trouble&quot;&gt;Staying Out of Trouble&lt;/h3&gt;

&lt;p&gt;There is always a fine line between meaningful concrete subclassing that aids understanding versus runaway subclassing and getting caught in an inheritance nightmare.&lt;/p&gt;

&lt;p&gt;Base (abstract) classes are a Ruby anti-pattern, yet are embraced by the Java community.&lt;/p&gt;

&lt;p&gt;Indeed, in statically typed languages there can be lots of boiler plate code which is ripe for inheritance. However, unless you’re a fairly experienced developer who is deft with an IDE then it’s so easy to become entangled amongst so many interfaces and implementations that you don’t know which way is up (I know because I’ve been in that situation before).&lt;/p&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;The concept of complete if-less programming has certainly left an impression on me. Not that I didn’t know that having good OO principles was desirable when designing software, I simply wasn’t &lt;em&gt;aware&lt;/em&gt; that there was a movement around this concept.&lt;/p&gt;

&lt;p&gt;I think that it’s easy - especially when writing non-production code - to subscribe to a ‘hack away’ mentality rather than properly think things through. Deadlines are often tight, testers are often lacking experience in writing clean, maintainable code and developers tasked with writing tests don’t always take it up with enthusiasm.&lt;/p&gt;

&lt;p&gt;But the fact remains that there is probably no better way of describing your system than through a solid set of BDD OO tests.&lt;/p&gt;
</description>
      <pubDate>Sun, 28 Apr 2013 00:00:00 +0000</pubDate>
      <link>http://bensnape.com/2013/04/28/if-less-programming/</link>
      <guid isPermaLink="true">http://bensnape.com/2013/04/28/if-less-programming/</guid>
    </item>
    
  </channel>
</rss>
