<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[devopsy]]></title>
  <link href="http://www.devopsy.com/atom.xml" rel="self"/>
  <link href="http://www.devopsy.com/"/>
  <updated>2014-09-26T12:47:18-04:00</updated>
  <id>http://www.devopsy.com/</id>
  <author>
    <name><![CDATA[Max Lincoln]]></name>
    <email><![CDATA[max@devopsy.com]]></email>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Some Swagger Projects]]></title>
    <link href="http://www.devopsy.com/blog/2014/09/26/some-swagger-projects/"/>
    <updated>2014-09-26T11:04:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2014/09/26/some-swagger-projects</id>
    <content type="html"><![CDATA[<p>I have an interest in ways of defining APIs that useful and easily shared between development, documentation and testing. I&#8217;ve been working on the Pacto contract-testing project for a while,
and more recently I was a member of the working group for the <a href="http://developers-blog.helloreverb.com/swagger-has-turned-2-0/">Swagger 2.0 release</a>.</p>

<p>Pacto itself will be switching to Swagger contracts in the near future. I plan to put out a v0.4.0 release that has initial support for Swagger and deprecates the current contract format, followed by v0.5.0 release with more complete Swagger support and that makes Swagger 2.0 the default contract format.</p>

<p>I&#8217;ve recently hacked together a few open-source projects related to that effort.</p>

<h3>Swagger.rb</h3>

<p>I&#8217;m releasing a <a href="https://github.com/swagger-rb/swagger-rb">Swagger parser</a> for Ruby. The goals are:</p>

<ul>
<li>Structurally and semantically validate Swagger documents (just like <a href="https://github.com/apigee-127/swagger-tools/issues/1">swagger-tools</a>)</li>
<li>Load Swagger documents</li>
<li>Provide a &#8220;flatter&#8221; API for iterating over Swagger&#8217;s hierarchical structure</li>
<li>Provide a builder to create Swagger contracts from other data</li>
</ul>


<p>Note: I&#8217;ve named the gem <a href="https://rubygems.org/gems/swagger-core">swagger-core</a>, because there is already <a href="https://rubygems.org/gems/swagger">a gem called swagger</a> (which is not related to the <a href="http://swagger.io">Swagger project</a>).</p>

<p>Source: <a href="https://github.com/swagger-rb/swagger-rb">https://github.com/swagger-rb/swagger-rb</a></p>

<h3>Swagger CLI</h3>

<p>I&#8217;ve been working on a <a href="https://github.com/swagger-rb/swagger-cli">CLI tool</a> that use the Swagger parser. The initial action I&#8217;m working on is a code generator that&#8217;s based on <a href="https://github.com/erikhuda/thor/wiki/Generators">Thor</a>, the same framework used by Rails and Padrino generators, and by the <a href="http://middlemanapp.com/">Middleman</a> static site generator. So this is also a proof of concept of how the Swagger parser could be integrated with those frameworks. Right now there is a template available for <a href="https://github.com/intridea/grape">Grape</a>, and I&#8217;ve also experimented with generating scripts to load a Mongo database for <a href="https://github.com/rackerlabs/pitchfork">Pitchfork</a>.</p>

<p>In the future I&#8217;ll probably add actions to validate, upgrade, or test Swagger APIs.</p>

<p>Source: <a href="https://github.com/swagger-rb/swagger-cli">https://github.com/swagger-rb/swagger-cli</a></p>

<h3>wadl2swagger</h3>

<p>I&#8217;ve been playing around with the OpenStack services, which are all defined using WADL. I wanted to be able equivalent Swagger to start playing around with, so I created a tool that converts from WADL to Swagger.</p>

<p>It does try to do a straightforward WADL->Swagger conversion whenever there is an equivalent concept, but many of the Swagger concepts don&#8217;t exist in the WADL standard. Sometimes it converts based on the conventions being followed in OpenStack WADLs, which include a mix of WADL and <a href="http://docbook.org/">docbook</a>. For example, WADL does not define a way to include example request/response data, but OpenStack documents this using docbook programlistings. So wadl2swagger will try to find these programlistings and convert them to Swagger example objects.</p>

<p>Source: <a href="https://github.com/rackerlabs/wadl2swagger">https://github.com/rackerlabs/wadl2swagger</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[DevOps Kata - Single Line of Code]]></title>
    <link href="http://www.devopsy.com/blog/2013/08/16/devops-kata-single-line-of-code/"/>
    <updated>2013-08-16T14:15:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2013/08/16/devops-kata-single-line-of-code</id>
    <content type="html"><![CDATA[<blockquote><p>Code Kata is an attempt to bring this element of practice to software development. A kata is an exercise in karate where you repeat a form many, many times, making little improvements in each. The intent behind code kata is similar.</p><footer><strong>Dave Thomas</strong> <cite><a href='http://codekata.com/kata/codekata-intro/'>Code Kata</a></cite></footer></blockquote>


<p>Since DevOps is a broad topic, it can be difficult to determine if a team has enough skills and is doing enough knowledge sharing to keep the <a href="http://en.wikipedia.org/wiki/Bus_factor">Bus Factor</a> low.  It can also be difficult for someone interested in learning to know where to start.  I thought I&#8217;d try to brainstorm some DevOps katas to give people challenges to learn and refine their skills.  If you&#8217;re worried about your bus factor, challenge less experienced team members to do these katas, imagining the senior team members are unavailable.</p>

<h2>Single Line of Code</h2>

<p>Goal: Deploy a change that involves a single line of code to production.</p>

<h2>Exercise</h2>

<p>If you have a non-trivial application, or set of related systems, then the time may vary depending on which line of code you touched.  So, here are some common examples to try:</p>

<ul>
<li>Change the title of your homepage</li>
<li>Change a line of code that is only executed once (i.e.: application initialization code)</li>
<li>Change a single line of code within a potential performance bottleneck</li>
<li>Change a line of code in your infrastructure automation (e.g., puppet, chef or ansible)</li>
</ul>


<h2>Honing the skill</h2>

<p>The corresponding question, &#8220;How long does it take to deploy a change that involves a single line of code to production?&#8221;, is often attributed to Mary and Tom Poppendieck, authors of <a href="http://www.poppendieck.com/">Lean Software Development</a>.  This makes sense, because a core principal of lean is &#8220;eliminate waste&#8221;, and minimizing the amount of actual work being performed as part of a change minimizes value-adding activities and exposes wasteful ones.</p>

<p>I do not suggest eliminating safeguards from small changes, but John Allspaw explained in <a href="http://www.slideshare.net/jallspaw/ops-metametrics-the-currency-you-pay-for-change">Ops Meta-Metrics</a> that small changes are fundamentally less risky.  You should still scrutinize small changes, but beware of processes that excessively scrutinizing a simple change.</p>

<h2>Watch for waste</h2>

<p>The kata should expose parts of the process that take longer than necessary.  Often there is a useful activity surrounded by wasteful overhead.</p>

<p>Code reviews, for example, are a useful activity but are often a magnet for wasteful process overhead.  A code review for a trivial change should only take a few minutes, but in some organizations scheduling the code review can take days.  Ask yourself:</p>

<ul>
<li>Did someone have to schedule a review, or was it pulled from &#8220;ready to review&#8221; queue?</li>
<li>Did the developer and reviewer both need to be present, or was the review done via asynchronous communication?</li>
<li>Are there enough available reviewers to process the queue quickly?</li>
<li>Were other useful activities (like automated testing or static code analysis) blocked while waiting for a code review?</li>
</ul>


<p>Similarly, the deployment itself can attract types of waste.  Make sure deploying does not involve:</p>

<ul>
<li>Manually running repetitive tasks</li>
<li>Waiting for someone to send you &#8220;the deploy commands&#8221;</li>
<li>Or searching documentation for deploy commands</li>
<li>Firefighting or redeploying because someone forgot a step</li>
</ul>


<p>The examples above are just a few of the many types of waste you might find.  The point is that while honing this kata you should keep (but optimize) any activities that make you more confident in the quality of the software you are releasing, but eliminate any associated overhead.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[MCollective is a chainsaw (not a hammer) - an experience report]]></title>
    <link href="http://www.devopsy.com/blog/2013/06/27/mcollective-is-a-chainsaw/"/>
    <updated>2013-06-27T13:37:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2013/06/27/mcollective-is-a-chainsaw</id>
    <content type="html"><![CDATA[<div class="alert alert-warning" role="alert"><em>Warning<em>: This is an archived post, the information below is outdated</div>


<h1>MCollective painpoints</h1>

<p>I was recently on team that ripped out MCollective halfway through a project.  Once it was removed, the deployments were much more stable, and when something did go wrong it was much easier to troubleshoot.
MCollective was given a fair chance - it was used for several months and was an essential piece of Continuous Delivery pipeline that deployed 4 apps and 7 microservices into a several environments.  It was getting the job done - but there were painpoints.</p>

<p>This is an email I sent to my team before MCollective was removed:</p>

<blockquote><h2>MCollective runs are non-deterministic</h2>

<p>If MCollective does not find a server within 2 seconds, it assumes it doesn&#8217;t exist.  If MCollective is down or the server is slow to respond the deployment will skip a server but &#8220;succeed&#8221; without even a warning.</p>

<h2>Swallowing errors</h2>

<p>I have seen many errors that should fail builds but are ignored.  This might be an implementation mistake rather than a framework issue - but the complexity of the framework makes this difficult to avoid.</p>

<h2>Difficult to change</h2>

<blockquote><h3>Changing agents</h3>

<p>Putting the commands on the agent side (instead of the invoker) makes them a little painful to change.  I think the process is
update git, wait for [the build to produce a new package], use mco to invoke puppet on all agents (this installs the agent change), run mco reload_agents or restart all agents.  Then, you can use the new command.  Sometimes the new agent fails to load.</p>

<h3>Changing facts</h3>

<p>Changing facts require a restart.  There was a corrupt facts file for two weeks before we MCollective was restarted and we found it.</p>

<h3>Failing silently</h3>

<p>If the facts or new agents are corrupt, MCollective logs the exception, then keeps running with the old agent/fact.  This hides the fact we&#8217;ve committed a flawed change.</p></blockquote>

<h2>No output flush for long-running jobs</h2>

<p>We aren&#8217;t flushing output - results do not show up in Go until the command has finished.  This causes Go to think the job has stalled (it can go many minutes without output when running Go).  I don&#8217;t think its possible to flush sooner, because of the way MCollective serializes responses.  MCollective was not designed for long-running jobs.  Queuing and Scheduling seem to have been dropped from the roadmap.  I also don&#8217;t think that stdout in a hash with whitespace escaped is very readable.</p>

<h2>Readability</h2>

<p>the way MCollective puts stdout and stderr in a hash, which messes with the formatting.</p>

<h2>Cost</h2>

<p>MCollective is more expensive than other options when the number of concurrent operations is low.  In theory it outscales other options, but that requires you to scale ActiveMQ (and Puppetmaster/db if you&#8217;re using them).  Right now our activemq is a single-point-of-failure, and our mco puppet runs die at 5 concurrent nodes.</p>

<h2>Install &amp; dev complexity</h2>

<p>MCollective is more difficult to install than alternatives.  We only have a working package for centos (and that is an outdated fork).  This makes it difficult to test MCollective changes locally.  Ideally I would have an mco client installed on my machine, and use it to test deployments (to a Vagrant VM running an Iba appnode), or to for the mco runbook (e.g.: checking facts).</p>

<p><strong>These are not hypothetical painpoints.  I have encountered every one of these problems with our current setup.</strong></p></blockquote>

<h1>Other reports</h1>

<p>A former colleague gave a good summary of MCollective:</p>

<blockquote><p>Capistrano for the most part relies on a central list of servers but it&#8217;s
also simpler because it&#8217;s just essentially ssh in a for loop. MCollective
can automatically discover the machines to execute commands in, which might
be a better fit for a very dynamic cloud environment, but it also adds a lot
of complexity with extra middleware.</p></blockquote>

<p>This is why I feel MCollective is often an over-engineering red flag.  Most projects are not &#8220;a very dynamic cloud environment&#8221;.</p>

<p>We were not the only team that had these sorts of problems.  A colleague at a different client wrote:</p>

<blockquote><p>My client has just replaced MCollective with a more hand rolled
solution as it recently took out our production site. I don&#8217;t have
many more details on why it went wrong - config mistakes or shonky
software, but they lost confidence in it.</p></blockquote>

<p>Perhaps it was config mistakes or shonky software.  If so, is it fair to lose confidence in MCollective?  Yes!  If a system is complex or difficult to understand, and you can get the same or better results with a similar system, then it is rational to be more confident in the simple system.  MCollective is a powerful tool, but it can obfuscate change management.  Non-deterministic runs, swallowing errors, not flushing output, non-readable output, complex for devs to test, complex security.  MCollective isn&#8217;t winning confidence.</p>

<h1>Wrapping up - MCollective is a chainsaw</h1>

<p>Does that mean you should never use MCollective?  No - but it shouldn&#8217;t be your go-to tool.  It&#8217;s not a hammer - its more like a chainsaw.  You can <a href="http://youtu.be/KVIVXUcepj8">do some amazing things with chainsaws</a>.  They get many jobs done much faster, but they lack finesse (you can&#8217;t see the fine details as you carve), and don&#8217;t work well for long-running tasks (they overheat).</p>

<p>MCollective is a very innovative niche tool.  Just realize that when you use it outside of its main niche, it may not be the simplest thing that could possibly work.  Personally I&#8217;ve found that <a href="http://www.devco.net/archives/2010/03/17/scheduling_puppet_with_MCollective.php">scheduling Puppet runs</a> <em>is not</em> a hard problem, but I usually go with <a href="http://www.jamescun.com/2012/12/14/masterless-puppet.html">masterless Puppet</a> and fancy SSH “for loop.”</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Is 118 equal to 90, 810, or 8A?]]></title>
    <link href="http://www.devopsy.com/blog/2013/05/14/is-118-equal-to-90/"/>
    <updated>2013-05-14T20:18:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2013/05/14/is-118-equal-to-90</id>
    <content type="html"><![CDATA[<p>I&#8217;m not a mathematician (if you are, please send some tips to improve this post), but I saw an interesting math problem going around the internet and I felt like posting my solution.  This the the problem as I first saw it:</p>

<p><img src="http://www.devopsy.com/images/posts/math_problem.png" title="For Geniuses" alt="If 111 = 13; 112 = 24; 113 = 35; 114 = 46; 115 = 57; Then 117 = ????"></p>

<p>Everyone seems to have solved this problem looking for a function that relates the two numbers.
I actually tried to solve the problem assuming the numbers were actually equal to each other (more on that later), but most people seem to have solved for a function that relates the numbers.  Let&#8217;s try looking for a function first.</p>

<h2>A simple solution</h2>

<p>If you&#8217;re looking for a simple pattern, you may notice the numbers on the left are increasing in increments of one, and the numbers on the right by increments of 11.  Mind the gap, though, we&#8217;re solving for 117 instead of 116.  Following this pattern we&#8217;ve increased by 2 on the left and should therefore add 22 on the right.  So the answer is 79.</p>

<p>This is just a <a href="http://en.wikipedia.org/wiki/Simple_linear_regression">simple linear regression</a>, so you can solve it to find a formula you can use for any number:</p>

<figure class='code'><figcaption><span>&#8220;Restated problem&#8221;  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='octave'><span class='line'><span class="n">y</span> <span class="p">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">bx</span>
</span><span class='line'>13 <span class="p">=</span> <span class="n">a</span> <span class="o">+</span> 11<span class="p">(</span>111<span class="p">)</span>
</span><span class='line'>13 <span class="p">=</span> <span class="n">a</span> <span class="o">+</span> 1221
</span><span class='line'>13 <span class="o">-</span> 1221 <span class="p">=</span> <span class="n">a</span>
</span><span class='line'><span class="n">a</span> <span class="p">=</span> 1208
</span><span class='line'>
</span><span class='line'><span class="n">y</span> <span class="p">=</span> 11<span class="n">x</span> <span class="o">-</span> 1208
</span></code></pre></td></tr></table></div></figure>


<p>There you go, a simple formula that works for all our test cases, and we can easily plug in 12345 and find that the answer with this pattern would be 134587.</p>

<h2>An alternative trick</h2>

<p>Some people used an alternative pattern.  They took the last digit from the right as the first digit on the left.  The remaining digit(s) on the right are the some of the digits on the right.  So for 113 the answer starts with 3 (the last number) and ends with 5 (the sum of 1, 1, and 3).  Again, this matches all the test cases.</p>

<h2>A different interpretation</h2>

<p>I was trying to solve a different problem.  I assumed the numbers were actually equal.  Of course 111 is not equal to 13 in our familiar <a href="http://en.wikipedia.org/wiki/Decimal">decimal</a> numeral system, but programmers are used to working with <a href="http://en.wikipedia.org/wiki/Radix">alternative bases</a> like <a href="http://en.wikipedia.org/wiki/Binary_number_system">binary or base 2</a>, <a href="http://en.wikipedia.org/wiki/Octal">octal or base 8</a> and <a href="http://en.wikipedia.org/wiki/Hexadecimal">hexadecimal or base 16</a>.</p>

<p>This is the pattern I found:</p>

<pre>
  111<sub>2</sub> = 13<sub>4</sub> = 7
  112<sub>3</sub> = 24<sub>5</sub> = 14
  113<sub>4</sub> = 35<sub>6</sub> = 23
  114<sub>5</sub> = 46<sub>7</sub> = 34
  115<sub>6</sub> = 57<sub>8</sub> = 47
</pre>


<p>If you continue this pattern:</p>

<pre>
  116<sub>7</sub> = 68<sub>9</sub> = 62
  117<sub>8</sub> = 79<sub>10</sub> = 79
  118<sub>9</sub> = 8A<sub>11</sub> = 98
</pre>


<p>(Note: Once you go past decimal, or base 10, the number system involves letters.  So A is the &#8220;number&#8221; after 9.)</p>

<h2>Comparison</h2>

<p>I find it interesting that three pretty simple patterns all work for 7 straight test cases, even though my interpretation solved a different problem!  However, they do begin diverging at 118.</p>

<div>
  <style>
     table.ztab1 {
        margin: auto;
        font-size: 70%;
        border: 1px solid black;
      }

      table.ztab1 th {
        font-weight: bold;
        background-color: steelblue;
        text-align: center;
        border-bottom: 2px solid black;
      }

      table.ztab1 th,td {
        padding: 4px 5px;
        border: 1px solid black;
      }

      table.ztab1 tr:nth-of-type(odd) {
        background-color: lightsteelblue;
      }

      table.ztab1 tr:nth-of-type(even) {
        background-color: cornflowerblue;
      }
  </style>
</div>




<table class="ztab1">
  <tbody>
    <tr>
      <td>x</td>
      <td>y (simple solution)</td>
      <td>y (composition)</td>
      <td>y (number systems)</td>
      <td>y (number systems in decimal)</td>
    </tr>
    <tr>
      <td>116</td>
      <td>68</td>
      <td>68</td>
      <td>68<sub>9</sub></td>
      <td>62</td>
    </tr>
    <tr>
      <td>117</td>
      <td>79</td>
      <td>79</td>
      <td>79<sub>10</sub></td>
      <td>79</td>
    </tr>
    <tr>
      <td>118</td>
      <td>90</td>
      <td>810</td>
      <td>8A<sub>11</sub></td>
      <td>98</sub></td>
    </tr>
  </tbody>
</table>




<p></p>


<p>The way the problem is stated is not precise.  The popular solutions assume there is an implicit function.  The problem could be more precisely stated as:</p>

<figure class='code'><figcaption><span>&#8220;Problem restated as a formula&#8221;  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='octave'><span class='line'><span class="n">Given</span><span class="p">:</span>
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>111<span class="p">)</span> <span class="p">=</span> 13
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>112<span class="p">)</span> <span class="p">=</span> 24
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>113<span class="p">)</span> <span class="p">=</span> 35
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>114<span class="p">)</span> <span class="p">=</span> 46
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>115<span class="p">)</span> <span class="p">=</span> 57
</span><span class='line'>
</span><span class='line'><span class="n">Find</span><span class="p">:</span>
</span><span class='line'>  <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span><span class='line'>  <span class="n">f</span><span class="p">(</span>117<span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>My solution assumes there are unknown implicit bases.  I&#8217;m not exactly sure how to state that, but it&#8217;s something like this:</p>

<pre>
Given:
  111<sub>f(x)</sub> = 13<sub>g(x)</sub>
  112<sub>f(x)</sub> = 24<sub>g(x)</sub>
  113<sub>f(x)</sub> = 35<sub>g(x)</sub>
  114<sub>f(x)</sub> = 46<sub>g(x)</sub>
  115<sub>f(x)</sub> = 57<sub>g(x)</sub>

Find:
  f(x)
  g(x)
  f(117) and g(117)
</pre>


<p>I&#8217;m curious where this problem originated.  <a href="http://en.wikipedia.org/wiki/Occam's_razor">Occam&#8217;s Razor</a> suggests they had the simple solution in mind, but if the problem is really &#8220;for genuises&#8221;, then counting by 11 is a bit trivial.  It&#8217;s too bad they didn&#8217;t ask about 118.If the problem was really &#8220;for genuises&#8221;, then it seems like there&#8217;d be a bit more to it than incrementing by 11.  They should have asked about 118.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Octopress on Cloud9]]></title>
    <link href="http://www.devopsy.com/blog/2012/10/04/octopress-on-cloud9/"/>
    <updated>2012-10-04T00:33:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2012/10/04/octopress-on-cloud9</id>
    <content type="html"><![CDATA[<p><em>This post was written, previewed, and published from <a href="http://c9.io/">http://c9.io/</a></em></p>

<p>I travel a lot, dual-boot, and own several computers.  It can be painful to maintain and sync identical development environments on each machine.  I have some projects that are okay to limit to one machine - but I should be able to blog
from anywhere.  This is actually a common reason I hear people choose WordPress over Octopress.</p>

<p>Cloud9 (<a href="http://c9.io/">http://c9.io/</a>) is an online IDE.  It&#8217;s matured quite a bit from when I first tried it.  It will integrate seamlessly with your GitHub account, and has a terminal so you can run ruby, python and other applications.  So, I
decided I to try it as an IDE for my blog.</p>

<p>Getting started was painless.  I just:</p>

<ul>
<li>Signed in to <a href="http://c9.io/">http://c9.io/</a> with my GitHub account.</li>
<li>Used the &#8220;Clone to Edit&#8221; button on the GitHub project for my blog.</li>
<li>Hit &#8220;Start Editing&#8221; once it was done.</li>
</ul>


<p>I could now edit posts on <a href="http://c9.io/">http://c9.io/</a> with Markdown syntax highlighting and interact directly with GitHub.  I wanted a bit more, though.  I wanted to preview my blog.</p>

<p>I made one very minor change to the Rakefile.  I had to change:</p>

<figure class='code'><figcaption><span>Rakefile</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='diff'><span class='line'> themes_dir      = &quot;.themes&quot;   # directory for blog files
</span><span class='line'> new_post_ext    = &quot;markdown&quot;  # default new post file extension when using the new_post task
</span><span class='line'> new_page_ext    = &quot;markdown&quot;  # default new page file extension when using the new_page task
</span><span class='line'><span class="gd">-server_port     = &quot;4000&quot;      # port for preview server eg. localhost:4000</span>
</span><span class='line'><span class="gi">+server_host     = ENV[&#39;IP&#39;] ||= &#39;0.0.0.0&#39;     # server bind address for preview server</span>
</span><span class='line'><span class="gi">+server_port     = ENV[&#39;PORT&#39;] ||= &quot;4000&quot;      # port for preview server eg. localhost:4000</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> desc &quot;Initial setup for Octopress: copies the default theme into the path of Jekyll&#39;s generator. Rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]&quot;
</span><span class='line'><span class="gu">@@ -78,7 +79,7 @@ task :preview do</span>
</span><span class='line'>   system &quot;compass compile --css-dir #{source_dir}/stylesheets&quot; unless File.exist?(&quot;#{source_dir}/stylesheets/screen.css&quot;)
</span><span class='line'>   jekyllPid = Process.spawn({&quot;OCTOPRESS_ENV&quot;=&gt;&quot;preview&quot;}, &quot;jekyll --auto&quot;)
</span><span class='line'>   compassPid = Process.spawn(&quot;compass watch&quot;)
</span><span class='line'><span class="gd">-  rackupPid = Process.spawn(&quot;rackup --port #{server_port}&quot;)</span>
</span><span class='line'><span class="gi">+  rackupPid = Process.spawn(&quot;rackup --host #{server_host} --port #{server_port}&quot;)</span>
</span><span class='line'>
</span><span class='line'>   trap(&quot;INT&quot;) {
</span><span class='line'>     [jekyllPid, compassPid, rackupPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH }
</span></code></pre></td></tr></table></div></figure>


<p>I&#8217;ve sent a <a href="https://github.com/imathis/octopress/pull/780">pull request</a>.  Hopefully this will work out-of-the-box with Octopress in the future.</p>

<p>Now, its easy to get your preview running.  Just run:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>bundle install
</span><span class='line'>rake preview
</span></code></pre></td></tr></table></div></figure>


<p>Soon, your preview should be running at <a href="http://&amp;lt;projectname&amp;gt;.&amp;lt;username&amp;gt;.c9.io/">http://&amp;lt;projectname&amp;gt;.&amp;lt;username&amp;gt;.c9.io/</a></code>.  It&#8217;s public, so you could even run a few tools against it, like the <a href="http://validator.w3.org/checklink">W3C link checker</a>.</p>

<p>Once you&#8217;re ready to post, just follow the normal instructions for deploying Octopress.  In my case it was:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>rake setup_github_pages
</span><span class='line'>rake deploy
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Conditional Traversals with Gremlin]]></title>
    <link href="http://www.devopsy.com/blog/2012/07/02/conditional-traversals-with-gremlin/"/>
    <updated>2012-07-02T08:00:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2012/07/02/conditional-traversals-with-gremlin</id>
    <content type="html"><![CDATA[<h2>The problem</h2>

<p>I recently did a spike for <a href="http://dev.creditunionfindr.org/2012/06/08/to-graph-or-not-to-graph/">CreditUnionFindr</a> that used a graph to determine if the user is eligable for a credit union&#8217;s <a href="http://www.depositaccounts.com/blog/credit-union-field-of-membership-requirements.html">Credit Union Field of Membership</a>.  We tried a several approaches but settled on graph-based approach.</p>

<p>The concept was simple: if you can traverse from the user to a credit union, they are eligable.  The majority of the Fields of Membership (FOMs) are simple, and a graph solution was trivial:
<img src="http://www.devopsy.com/images/posts/simple-affiliation.png" title="Simple Affiliation" alt="Max works at ThoughtWorks which qualifies for TW Credit Union"></p>

<p>However, some FOMs are more complex.  We were concerned about
<a href="http://paulhammant.com/2012/04/15/application-development-glass-ceilings/">Glass Ceilings</a> so we needed to be sure our graph was flexible.</p>

<p>One problem we hit was conditional traversals.  Occasionally we needed to do something like this:
<img src="http://www.devopsy.com/images/posts/conditional-affiliation.png" title="Conditional Affiliation" alt="Max works at ThoughtWorks which qualifies for TW Credit Union if employment duration is greater than 1 year"></p>

<p>I found a few examples of conditional traversals in graphs, but the traverser always knew the condition and often the depth where it occurred.  If that was the case, we could use this naive solution for the above graph:</p>

<figure class='code'><figcaption><span>EligabilityGraph.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="nf">dumbSolve</span><span class="o">(</span><span class="n">Object</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">results</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="na">v</span><span class="o">(</span><span class="n">id</span><span class="o">).</span><span class="na">outE</span><span class="o">.</span><span class="na">filter</span><span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">duration</span> <span class="o">&gt;</span> <span class="mi">365</span><span class="o">}.</span><span class="na">inV</span><span class="o">.</span><span class="na">outE</span><span class="o">.</span><span class="na">inV</span><span class="o">.</span><span class="na">paths</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">name</span><span class="o">}</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">description</span><span class="o">}</span>
</span><span class='line'>    <span class="n">solutions</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="na">toList</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>This wouldn&#8217;t have worked well for us.  If we had to know all the conditions (and depths) ahead of time our solution would not be as simple as we originally envisioned.  We were looking for a simple traversal that could solve a more complex graph like this:
<img src="http://www.devopsy.com/images/posts/complex_graph.png">
This graph contains two unconditional qualifications and two conditional qualifications.  The two conditions (not shown) could be different.</p>

<p>The concept was still simple - traverse from the user to credit unions, avoiding nodes with false conditions.  We needed the conditions to be closures to do this without overcomplicating our traversal.</p>

<h2>Our solution</h2>

<p>We came up with something like this:</p>

<figure class='code'><figcaption><span>EligibilityGraph.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">def</span> <span class="nf">solve</span><span class="o">(</span><span class="n">Object</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">def</span> <span class="n">results</span> <span class="o">=</span>
</span><span class='line'><span class="c1">//            Find the User node and save it as u</span>
</span><span class='line'>        <span class="n">g</span><span class="o">.</span><span class="na">v</span><span class="o">(</span><span class="n">id</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s1">&#39;u&#39;</span><span class="o">).</span><span class="na">sideEffect</span><span class="o">{</span><span class="n">u</span> <span class="o">=</span> <span class="n">it</span><span class="o">}</span>
</span><span class='line'><span class="c1">//            Save the edge before the condition as l</span>
</span><span class='line'>            <span class="o">.</span><span class="na">outE</span><span class="o">.</span><span class="na">sideEffect</span><span class="o">{</span><span class="n">l</span> <span class="o">=</span> <span class="n">it</span><span class="o">}</span>
</span><span class='line'><span class="c1">//            Save the edge to be filtered as x</span>
</span><span class='line'>            <span class="o">.</span><span class="na">inV</span><span class="o">.</span><span class="na">outE</span><span class="o">.</span><span class="na">sideEffect</span><span class="o">{</span><span class="n">x</span> <span class="o">=</span> <span class="n">it</span><span class="o">}</span>
</span><span class='line'><span class="c1">//            Filter based on the &quot;condition&quot; closure</span>
</span><span class='line'>            <span class="o">.</span><span class="na">filter</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">condition</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="k">this</span><span class="o">.</span><span class="na">evaluate</span><span class="o">(</span><span class="s2">&quot;${it.condition}&quot;</span><span class="o">)}</span>
</span><span class='line'><span class="c1">//            Keep going until we find a Credit Union</span>
</span><span class='line'>            <span class="o">.</span><span class="na">inV</span><span class="o">.</span><span class="na">loop</span><span class="o">(</span><span class="s1">&#39;u&#39;</span><span class="o">)</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">object</span><span class="o">.</span><span class="na">type</span> <span class="o">!=</span> <span class="s1">&#39;CU&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">it</span><span class="o">.</span><span class="na">loops</span> <span class="o">&lt;</span> <span class="mi">50</span><span class="o">}</span>
</span><span class='line'><span class="c1">//            Format our results</span>
</span><span class='line'>            <span class="o">.</span><span class="na">paths</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">name</span><span class="o">}</span> <span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">description</span><span class="o">}</span>
</span><span class='line'>    <span class="n">solutions</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="na">toList</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The example above is Gremlin Groovy.  You do need a minor trick in Groovy.  The class containing the solve method needs to be able to call evaluate with the current context.  I accomplished this by extending GroovyShell.</p>

<p>You should be able to use this technique with various languages and databases by using the <a href="https://github.com/tinkerpop/rexster/wiki/Gremlin-Extension" title="Rexster Gremlin Extension">Rexster Gremlin Extension</a> or the <a href="https://github.com/neo4j-contrib/gremlin-plugin">Neo4J Gremlin Plugin</a>.</p>

<h2>Sample Test</h2>

<p>Graphs conveniently give us paths, so we can get information about the final result, or about the path to the result.  In this case, we can easily turn the path into a human readable explanation of eligibility.  Hopefully that makes the example test below easy to follow.</p>

<figure class='code'><figcaption><span>EligibilityGraph.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">getDisplayPaths</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span><span class="o">(</span><span class="n">solutions</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">IllegalStateException</span><span class="o">(</span><span class="s2">&quot;Solve first&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">solutions</span><span class="o">.</span><span class="na">collect</span><span class="o">{</span><span class="n">it</span><span class="o">.</span><span class="na">join</span><span class="o">(</span><span class="s1">&#39; &#39;</span><span class="o">)}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">getEligibleCUs</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span><span class="o">(</span><span class="n">solutions</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">IllegalStateException</span><span class="o">(</span><span class="s2">&quot;Solve first&quot;</span><span class="o">)</span>
</span><span class='line'>    <span class="n">solutions</span><span class="o">.</span><span class="na">collect</span><span class="o">{</span><span class="n">it</span><span class="o">[-</span><span class="mi">1</span><span class="o">]}.</span><span class="na">unique</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>ComplexGraphTest.groovy</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kt">void</span> <span class="nf">testMaxWrongDegree</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">g</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="s1">&#39;Max_Drexel&#39;</span><span class="o">).</span><span class="na">degree</span> <span class="o">=</span> <span class="s1">&#39;BSCS&#39;</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">results</span> <span class="o">=</span> <span class="n">eg</span><span class="o">.</span><span class="na">solve</span><span class="o">(</span><span class="s1">&#39;Max&#39;</span><span class="o">)</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">paths</span> <span class="o">=</span> <span class="n">eg</span><span class="o">.</span><span class="na">getDisplayPaths</span><span class="o">()</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">creditUnions</span> <span class="o">=</span> <span class="n">eg</span><span class="o">.</span><span class="na">getEligibleCUs</span><span class="o">()</span>
</span><span class='line'>        <span class="n">assertTrue</span><span class="o">(</span><span class="n">paths</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s1">&#39;Max works at ThoughtWorks which qualifies for TW Credit Union&#39;</span><span class="o">))</span>
</span><span class='line'>        <span class="n">assertTrue</span><span class="o">(</span><span class="n">paths</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s1">&#39;Max lives at NYC which qualifies for Big Apple Credit Union&#39;</span><span class="o">))</span>
</span><span class='line'>        <span class="n">assertTrue</span><span class="o">(</span><span class="n">paths</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s1">&#39;Max lives in Manhattan which qualifies for Big Apple Credit Union&#39;</span><span class="o">))</span>
</span><span class='line'>        <span class="n">assertEquals</span><span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="n">results</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
</span><span class='line'>        <span class="n">assertEquals</span><span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="n">creditUnions</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>You can find the full code and more tests on <a href="https://github.com/maxlinc/gremlin-experiments">GitHub</a></p>

<h2>Summary</h2>

<p>Someone asked <a href="https://groups.google.com/forum/?fromgroups#!topic/gremlin-users/Ux-ACYmwCR4">&#8220;How much logic should we be putting into the queries we run through Gremlin?&#8221;</a> in the Gremlin group and the consensus was &#8220;minimal&#8221;.  This technique goes against that somewhat.  On the other hand we&#8217;ve actually pushed most of the logic out of the traversal - we just moved the logic into the graph rather than the post-processing of results.</p>

<p>The best use cases for graphs called out in <a href="http://www.amazon.com/NoSQL-Distilled-Emerging-Polyglot-Persistence/dp/0321826620">NoSQL Distilled</a> are Social graphs; Routing, Dispatch and Location based services, and Recommendation engines.  Our spike fell outside those categories, so the techniques that worked for us might not make sense for more typical graph projects.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Numeric Indexes and the Neo4J REST server]]></title>
    <link href="http://www.devopsy.com/blog/2012/04/19/numeric-indexes-and-the-neo4j-rest-server/"/>
    <updated>2012-04-19T10:25:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2012/04/19/numeric-indexes-and-the-neo4j-rest-server</id>
    <content type="html"><![CDATA[<p>Suppose your neo database contains suppliers for custom t-shirt printing.  Some suppliers have a minimum order quantity, and you want to quickly lookup suppliers that would accept a given quantity.  This is easy with the Embedded Neo4J server in Java:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'>  <span class="c1">// Add to an index</span>
</span><span class='line'>  <span class="n">Index</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">suppliers</span> <span class="o">=</span> <span class="n">graphDb</span><span class="o">.</span><span class="na">index</span><span class="o">().</span><span class="na">forNodes</span><span class="o">(</span><span class="s">&quot;suppliers&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="n">suppliers</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">someSupplier</span><span class="o">,</span> <span class="s">&quot;minimum-order&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="n">ValueContext</span><span class="o">(</span><span class="mi">5</span><span class="o">).</span><span class="na">indexNumeric</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Test query with a match</span>
</span><span class='line'>  <span class="n">QueryContext</span> <span class="n">queryContext</span> <span class="o">=</span> <span class="n">QueryContext</span><span class="o">.</span><span class="na">numericRange</span><span class="o">(</span><span class="s">&quot;minimum-order&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="mi">8</span><span class="o">);</span>
</span><span class='line'>  <span class="n">IndexHits</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">potentialSuppliers</span> <span class="o">=</span> <span class="n">suppliers</span><span class="o">.</span><span class="na">query</span><span class="o">(</span><span class="n">queryContext</span><span class="o">);</span>
</span><span class='line'>  <span class="n">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">potentialSuppliers</span><span class="o">.</span><span class="na">size</span><span class="o">());</span>
</span><span class='line'>  <span class="n">assertEquals</span><span class="o">(</span><span class="n">someSupplier</span><span class="o">,</span> <span class="n">potentialSuppliers</span><span class="o">.</span><span class="na">getSingle</span><span class="o">());</span>
</span><span class='line'>  <span class="c1">// Test query without a match</span>
</span><span class='line'>  <span class="n">queryContext</span> <span class="o">=</span> <span class="n">QueryContext</span><span class="o">.</span><span class="na">numericRange</span><span class="o">(</span><span class="s">&quot;minimum-order&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="mi">4</span><span class="o">);</span>
</span><span class='line'>  <span class="n">potentialSuppliers</span> <span class="o">=</span> <span class="n">suppliers</span><span class="o">.</span><span class="na">query</span><span class="o">(</span><span class="n">queryContext</span><span class="o">);</span>
</span><span class='line'>  <span class="n">assertEquals</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">potentialSuppliers</span><span class="o">.</span><span class="na">size</span><span class="o">());</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, those <a href="http://docs.neo4j.org/chunked/stable/indexing-lucene-extras.html">numeric ranges</a> are not supported by Neo4J REST server.  It&#8217;s probably best to write a plugin for this, but if you cannot use custom plugins (for example, I don&#8217;t think the Heroku Neo4J Add-On allows custom plugins) then the Neo4J <a href="https://github.com/tinkerpop/gremlin/wiki">Gremlin Plugin</a> may be your best choice.</p>

<p>Here&#8217;s how you would do the same thing if you&#8217;re using Ruby&#8217;s <a href="https://github.com/maxdemarzi/neography">Neography</a>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">NeoQuery</span>
</span><span class='line'>  <span class="no">GREMLIN_NUMERIC_INDEX_TEMPLATE</span> <span class="o">=</span> <span class="o">&lt;&lt;</span><span class="no">eos</span>
</span><span class='line'><span class="sh">    import org.neo4j.index.lucene.*;</span>
</span><span class='line'><span class="sh">    neo4j = g.getRawGraph();</span>
</span><span class='line'><span class="sh">    tx = neo4j.beginTx();</span>
</span><span class='line'><span class="sh">    idxManager = neo4j.index();</span>
</span><span class='line'><span class="sh">    cuIndex = idxManager.forNodes(index_name);</span>
</span><span class='line'><span class="sh">    node = neo4j.getNodeById(Long.parseLong(node_id));</span>
</span><span class='line'><span class="sh">    cuIndex.add(node, key_name, new ValueContext(value).indexNumeric());</span>
</span><span class='line'><span class="sh">    tx.success();</span>
</span><span class='line'><span class="sh">    tx.finish();</span>
</span><span class='line'><span class="no">eos</span>
</span><span class='line'>
</span><span class='line'>  <span class="no">GREMLIN_NUMERIC_QUERY_TEMPLATE</span> <span class="o">=</span> <span class="o">&lt;&lt;</span><span class="no">eos</span>
</span><span class='line'><span class="sh">    import org.neo4j.index.lucene.*;</span>
</span><span class='line'><span class="sh">    neo4j = g.getRawGraph();</span>
</span><span class='line'><span class="sh">    idxManager = neo4j.index();</span>
</span><span class='line'><span class="sh">    cuIndex = idxManager.forNodes(index_name);</span>
</span><span class='line'><span class="sh">    cuIndex.query(QueryContext.numericRange(key_name, null, value, false, false));</span>
</span><span class='line'><span class="no">eos</span>
</span><span class='line'>
</span><span class='line'>  <span class="vc">@@neo</span> <span class="o">=</span> <span class="no">Neography</span><span class="o">::</span><span class="no">Rest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">ENV</span><span class="o">[</span><span class="s2">&quot;NEO4J_URL&quot;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&quot;http://localhost:7474&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">index_numeric</span><span class="p">(</span><span class="n">index_name</span><span class="p">,</span> <span class="n">node</span><span class="p">,</span> <span class="n">key_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</span><span class='line'>    <span class="vc">@@neo</span><span class="o">.</span><span class="n">execute_script</span><span class="p">(</span><span class="no">GREMLIN_NUMERIC_INDEX_TEMPLATE</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'>      <span class="ss">:index_name</span> <span class="o">=&gt;</span> <span class="n">index_name</span><span class="p">,</span>
</span><span class='line'>      <span class="ss">:key_name</span> <span class="o">=&gt;</span> <span class="n">key_name</span><span class="p">,</span>
</span><span class='line'>      <span class="ss">:node_id</span> <span class="o">=&gt;</span> <span class="n">node</span><span class="p">,</span>
</span><span class='line'>      <span class="ss">:value</span> <span class="o">=&gt;</span> <span class="n">value</span><span class="p">})</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">query_numeric</span><span class="p">(</span><span class="n">index_name</span><span class="p">,</span> <span class="n">key_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</span><span class='line'>    <span class="vc">@@neo</span><span class="o">.</span><span class="n">execute_script</span><span class="p">(</span><span class="no">GREMLIN_NUMERIC_QUERY_TEMPLATE</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'>      <span class="ss">:index_name</span> <span class="o">=&gt;</span> <span class="n">index_name</span><span class="p">,</span>
</span><span class='line'>      <span class="ss">:key_name</span> <span class="o">=&gt;</span> <span class="n">key_name</span><span class="p">,</span>
</span><span class='line'>      <span class="ss">:value</span> <span class="o">=&gt;</span> <span class="n">value</span><span class="p">})</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># Add to index</span>
</span><span class='line'><span class="no">NeoQuery</span><span class="o">.</span><span class="n">index_numeric</span><span class="p">(</span><span class="s1">&#39;suppliers&#39;</span><span class="p">,</span> <span class="n">your_node</span><span class="o">.</span><span class="n">neo_id</span><span class="p">,</span> <span class="ss">:minimum_order_size</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
</span><span class='line'><span class="c1"># Test query with a match</span>
</span><span class='line'><span class="n">potential_suppliers</span> <span class="o">=</span> <span class="no">NeoQuery</span><span class="o">.</span><span class="n">query_numeric</span><span class="p">(</span><span class="s1">&#39;suppliers&#39;</span><span class="p">,</span> <span class="ss">:minimum_order_size</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
</span><span class='line'><span class="n">potential_suppliers</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="mi">1</span>
</span><span class='line'><span class="no">Neography</span><span class="o">::</span><span class="no">Node</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">potential_suppliers</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">neo_id</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="n">your_node</span><span class="o">.</span><span class="n">neo_id</span>
</span><span class='line'><span class="c1"># Test query without a match</span>
</span><span class='line'><span class="n">potential_suppliers</span> <span class="o">=</span> <span class="no">NeoQuery</span><span class="o">.</span><span class="n">query_numeric</span><span class="p">(</span><span class="s1">&#39;suppliers&#39;</span><span class="p">,</span> <span class="ss">:minimum_order_size</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
</span><span class='line'><span class="n">potential_suppliers</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="mi">0</span>
</span></code></pre></td></tr></table></div></figure>


<p><em>Disclaimer: I have not tested behavior in a clustered environment and you should consider security before using execute_script.</em></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Commit often - and update your dependencies!]]></title>
    <link href="http://www.devopsy.com/blog/2012/03/11/commit-often-and-update-your-dependencies/"/>
    <updated>2012-03-11T22:49:00-04:00</updated>
    <id>http://www.devopsy.com/blog/2012/03/11/commit-often-and-update-your-dependencies</id>
    <content type="html"><![CDATA[<p>How often do you commit your changes?
How often do you upgrade your dependencies?</p>

<h2>Commit Often</h2>

<p>There is a common answer to the first question:</p>

<blockquote><p>Everyone Commits To the Mainline Every Day</p><footer><strong>Martin Fowler</strong> <cite><a href='http://www.martinfowler.com/articles/continuousIntegration.html'>Continuous Integration</a></cite></footer></blockquote>




<blockquote><p>Code is integrated and tested after a few hours-a day of development at most.</p><footer><strong>Kent Beck</strong> <cite>Extreme Programming Explained</cite></footer></blockquote>




<blockquote><p>We recommend that you aim to commit changes to the version control system at the conclusion of each separate incremental change or refactoring.  If you use this technique correctly, you should be checking in at the very minimum once a day, and more usually several times a day.</p><footer><strong>Jez Humble and David Farley</strong> <cite>Continuous Delivery</cite></footer></blockquote>


<p>This makes sense: small frequent merges are easier to integrate than large, occasional ones.  The less often you commit (to a shared &#8220;trunk&#8221; branch) the more painful integration becomes.  You find problems too late, and spend a lot of time in integration or merge hell: performing painful merges or grepping through large changesets to find the needle in the haystack that broke the app.</p>

<h2>Upgrade Often</h2>

<p>The same logic applies to upgrading dependencies.  If you upgrade frequently, each individual upgrade is usually quick and painless.  Yet there is less consensus about how often to upgrade.</p>

<p>I&#8217;m a fan of small, frequent upgades.  I consider any outdated library to be tech debt.  You don&#8217;t always need to address it right away but you should review it and make an informed decision.  This is the difference between Reckless/Inadvertant debt and Prudent/Deliberate debt.</p>

<p>I&#8217;m not a fan of Long-Term Support (LTS) releases either.  It means you&#8217;re setup up for a <em>major</em> upgrade, you&#8217;re missing out on faster/safer/cooler software.  Your environment probably isn&#8217;t as static as you pretend either - why would you use a locked version of Selenium/WebDriver unless you&#8217;ve turned off Firefox/Chrome updates.</p>

<h3>Uninformed Pessimism</h3>

<p>Can a team simultaneously be &#8220;bleeding edge&#8221; and a &#8220;late adopter&#8221;?</p>

<p>I&#8217;ve seen it.  I was on a team that had used an a beta release of a library, then skipped the next few stable releases.  When we finally upgraded a method was missing.  I dug through the release notes for several versions, looking for a deprecation notice with a recommended alternative.  I never found one - because the method never made it out of the beta!</p>

<p>The team did not choose to do this - they were simple uninformed.  They&#8217;d been inadvertently reckless.  This is a trap you can fall in even if you don&#8217;t use beta versions.  If you don&#8217;t have a good dependency report that shows what you&#8217;re using <em>and</em> what upgrades are available then you are uninformed.  You could try to manually assemble a report, but that is impractical on large projects (most Java projects), or projects with lots of small, frequently released libraries (most Ruby projects).</p>

<h3>Informed Pessimism</h3>

<p>The first step towards better dependency management is becoming is &#8220;Informed Pessimism&#8221;: generate and regularly review a report of available upgrades.  Many package managers have a report or command you can use:</p>

<ul>
<li><a href="http://mojo.codehaus.org/versions-maven-plugin/dependency-updates-report-mojo.html">mvn versions:dependency-updates-report</a></li>
<li><a href="https://github.com/scoop/bundle_outdated">bundle_outdated</a></li>
<li><code>gem outdated</code></li>
<li><code>yum list updates</code></li>
<li><code>apt-get -s dist-upgrade</code></li>
</ul>


<p>Bundler usually beats Maven, but in this case the Maven plugin generates a nice report I can display from a CI server.  Here&#8217;s a <a href="http://mojo.codehaus.org/versions-maven-plugin/dependency-updates-report.html">live examle</a>.</p>

<h3>Cautious Optimism</h3>

<p>Alex Chaffee proposed <a href="https://docs.google.com/View?id=dnqrs8b_51d7q4b7mr&amp;pli=1">Cautious Optimism</a> as the ideal.  A Cautious Optimism build system will attempt to upgrade dependencies as soon as possible.</p>

<p>Cautious Optimism isn&#8217;t for everyone, but if you have good dependency management and a Continuous Delivery setup you can trust your pipeline to catch problems introduced by an upgrade.  If a problem is found, you lock the dependency to the last-known good version until a solution is found.  Some tools that are useful in implementing Cautious Optimism:</p>

<ul>
<li><a href="https://wiki.jenkins-ci.org/display/JENKINS/Maven+Dependency+Update+trigger">Jenkins Maven Dependency Update trigger</a></li>
<li><a href="http://mojo.codehaus.org/versions-maven-plugin/use-latest-versions-mojo.html">mvn versions:use-latest-versions</a></li>
<li><a href="http://rubygems.org/gems/bundler-auto-update">bundler-auto-update</a></li>
</ul>


<h3>Matrix Builds</h3>

<p>If your team can release frequently it can probably upgrade frequently.  The only exceptions may be upgrades to large frameworks - Java/Spring, Ruby/Rails, etc.  It may be prudent to delay an upgrade even if its possible - just because it means a long downloads and a lot of knew features to study.</p>

<p>It is possible to get the feedback without actually committing to an upgrade.  I view this as using your CI/CD setup to answer two questions:</p>

<ul>
<li>Is the app releasable? (Test with fixed dependencies)</li>
<li>Is the app upgradable? (Test with latest dependencies)</li>
</ul>


<p>I&#8217;d probably do the upgradable check less often.  The releasabilty checks should be every commit, but you can probably check for upgradability nightly.</p>

<h3>Conclusion</h3>

<p>Most Agile teams believe in the &#8220;commit often&#8221; motto.  You should remember that <em>everyone</em> is supposed to commit often.  Unless everyone has integrated recently, someone on the team may still be headed towards merge hell.</p>

<p>I consider third-party library developers to be part of the extended team.  Unfortunately these developers cannot push their own changes and cannot resolve the conflicts.  The core team needs to be proactive about reviewing and pulling upgrades as often as practical.  Every day may not be practical, but try to avoid skipping releases.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Continuous Deployment to Heroku with Jenkins]]></title>
    <link href="http://www.devopsy.com/blog/2012/01/20/continuous-deployment-to-heroku-with-jenkins/"/>
    <updated>2012-01-20T14:44:00-05:00</updated>
    <id>http://www.devopsy.com/blog/2012/01/20/continuous-deployment-to-heroku-with-jenkins</id>
    <content type="html"><![CDATA[<p>Heroku is an easy way to host your apps.  It is simple and runs in the cloud - so you avoid the need for servers and a lot of infrastructure automation scripting.  Unfortunately, there aren&#8217;t
many good Continuous Integration or Continuous Delivery options that can run on Heroku<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>, so if you&#8217;re a firm believer in CI/CD you will still need some servers outside of Heroku.</p>

<p>So, how do you add Heroku deployments into you pipeline if you are running a CI server outside heroku?  This Jenkins setup is working for me:</p>

<h2>Heroku Setup</h2>

<p>If you don&#8217;t already have multiple environments in Heroku, you&#8217;ll need to set that up.  Check out the Heroku guide on <a href="http://devcenter.heroku.com/articles/multiple-environments">Managing Multiple Environments for an App</a>.</p>

<p>tl;dr: Heroku environments are really distinct apps.  They each have their own set of plugins and collaborators, so make two apps with the same plugins.  I wouldn&#8217;t share the collaborators - the team can push to CI, but only
CI should push to production.</p>

<p>Here&#8217;s an sample of a two-environment setup:</p>

<figure class='code'><figcaption><span>Heroku setup</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>heroku create --stack cedar --addons scheduler your-app
</span><span class='line'>heroku create --stack cedar --addons scheduler --remote production your-app-prod
</span></code></pre></td></tr></table></div></figure>


<h2>Jenkins Setup</h2>

<p>Here&#8217;s what you need to do on the Jenkins side:</p>

<h3>Plugins</h3>

<p>Install <a href="https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin">Jenkins GIT plugin</a></p>

<h3>Create the Post Deploy Job</h3>

<p>Create a new job named your-app-postdeploy.  It should run whatever is necessary to complete a deployment after a git push to Heroku.  Probably something along the lines of:</p>

<figure class='code'><figcaption><span>Jenkins Execute shell Setting</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>heroku rake db:migrate db:seed --app your-app-prod
</span></code></pre></td></tr></table></div></figure>


<h3>Setup both Git repos as SCMs</h3>

<p><a href="http://www.devopsy.com/images/posts/jenkins-heroku-scm.png"><img src="http://www.devopsy.com/images/posts/jenkins-heroku-scm.png" title="Jenkins SCM Setup" alt="Git Repositories Repository URL git@heroku.com:your-app.git Repository URL git@heroku.com:your-app-prod.git Name production Branches to build Branch Specifier (blank for default): "></a></p>

<h3>Setup the build</h3>

<p>Setup whatever your CI would normally do if you weren&#8217;t using Heroku environments.  If your CI tests include an integration phase that hits <a href="http://your-app.heroku.com">http://your-app.heroku.com</a> then you should probably include the same steps as your post-deploy job (with &#8211;app your-app).</p>

<h3>Setup the merge and push</h3>

<p><a href="http://www.devopsy.com/images/posts/jenkins-heroku-merge.png"><img src="http://www.devopsy.com/images/posts/jenkins-heroku-merge.png" title="Jenkins Git Publisher" alt="Git Publisher Push Only If Build Succeeds true Merge Results true Branches Branch to push master Target remote name production"></a></p>

<h3>Setup the post-deploy trigger</h3>

<p><a href="http://www.devopsy.com/images/posts/jenkins-heroku-post-trigger.png"><img src="http://www.devopsy.com/images/posts/jenkins-heroku-post-trigger.png" title="Jenkins Build Other Projects" alt="Build other projects Projects to build your-app-postdeploy Trigger only if build succeeds"></a></p>

<h2>Summary</h2>

<p>This should get you Continuous Deployment from Jenkins to Heroku.  There are a couple caveats:</p>

<ul>
<li>You may get a merge conflict if someone manually pushes changes to production that were not pushed through your-app/master.  You shouldn&#8217;t do that anyways.</li>
<li>This is Continuous Deployment, not Continuous Delivery.  You would need to make some changes to support a manual gate before production.  The only opportunity this provides for a manual gate (between deployment and post-deployment) is not a viable option.</li>
<li>Your application may be broken between the deploy and post-deploy.  This is usually just a few seconds.  You could briefly enable Heroku <a href="http://devcenter.heroku.com/articles/maintenance-mode">maintenance mode</a> if the user experience is an issue.</li>
</ul>

<div class="footnotes">
<hr/>
<ol>
<li id="fn:1">
<p>Travis-CI is one.  It only seems to support public GitHub repos and is still in alpha.  Tddium is another.  It is a paid service and Heroku does not currently recommend allowing it to push to production.<a href="#fnref:1" rev="footnote">&#8617;</a></p></li>
</ol>
</div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Setting up ssh known hosts via Capistrano]]></title>
    <link href="http://www.devopsy.com/blog/2012/01/12/setting-up-ssh-known-hosts-via-capistrano/"/>
    <updated>2012-01-12T00:58:00-05:00</updated>
    <id>http://www.devopsy.com/blog/2012/01/12/setting-up-ssh-known-hosts-via-capistrano</id>
    <content type="html"><![CDATA[<p>Puppet and Chef are both good at managing SSH known_hosts.  This is the primary example for Puppet&#8217;s <a href="http://docs.puppetlabs.com/guides/exported_resources.html">Exported Resources</a>, and Chef does it easily via <a href="https://github.com/opscode/cookbooks/blob/master/ssh_known_hosts/recipes/default.rb">search</a>.</p>

<p>However, neither solution works well in a &#8220;masterless&#8221; setup.  The Chef solution requires a full Chef Server setup - CouchDB, AMQP, and Solr.  Puppet isn&#8217;t quite as bad - you just need a database to run masterless and still use Exported Resources - like <a href="http://semicomplete.com/presentations/puppet-at-loggly/puppet-at-loggly.pdf.html">Loggly does</a>.  This negates some of the masterless benefits, though, and Loggly lists lots of caveats.</p>

<p>If you happen to be using Capistrano for any part of your project, here is a fast, simple way to manage known_hosts without requiring a database.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">task</span> <span class="ss">:setup_known_hosts</span> <span class="k">do</span>
</span><span class='line'>        <span class="n">find_servers</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">h</span><span class="o">|</span>
</span><span class='line'>          <span class="n">run</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">sudo</span><span class="si">}</span><span class="s2"> bash -c &#39;ssh-keyscan -t rsa </span><span class="si">#{</span><span class="n">h</span><span class="si">}</span><span class="s2"> &gt;&gt; /etc/ssh/ssh_known_hosts&#39;&quot;</span>
</span><span class='line'>        <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Usage is simple, just:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>cap setup_known_hosts
</span></code></pre></td></tr></table></div></figure>


<p>or</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>cap setup_known_hosts <span class="nv">HOSTS</span><span class="o">=</span>&lt;your_hosts&gt;
</span></code></pre></td></tr></table></div></figure>


<p>This was a good fit for us.  We were using Capistrano for bootstrapping, and <a href="https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension">Capistrano Multistage Extension</a> to define environments.  I just added this task as part of bootstrapping, so <code>cap production bootstrap</code> would allow all my production servers to talk with each other - but no one else.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Launched!]]></title>
    <link href="http://www.devopsy.com/blog/2012/01/12/launched/"/>
    <updated>2012-01-12T00:22:00-05:00</updated>
    <id>http://www.devopsy.com/blog/2012/01/12/launched</id>
    <content type="html"><![CDATA[<p>I&#8217;m finally getting a blog started.  I&#8217;m dual booting Windows and Ubuntu, and had to fix rubypython on Windows (so Octopress can use Pygments for syntax highlighting).  I took <a href="https://github.com/halostatue/rubypython/pull/1">this pull request</a> and modified it slightly to work with newer Python installers.</p>

<figure class='code'><figcaption><span>This is how I fixed syntax highlighting lib/rubypython/pythonexec.rb</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">if</span> <span class="no">FFI</span><span class="o">::</span><span class="no">Platform</span><span class="o">.</span><span class="n">windows?</span>
</span><span class='line'>  <span class="c1"># Do this after trying to add alternative extensions, </span>
</span><span class='line'>  <span class="c1"># since windows install has a python27.a and can cause </span>
</span><span class='line'>  <span class="c1"># troble.</span>
</span><span class='line'>  <span class="c1">#</span>
</span><span class='line'>  <span class="c1"># Some Windows python installers install the DLL in the python directory</span>
</span><span class='line'>  <span class="c1"># others install in the Windows system directory.  So we should search both.</span>
</span><span class='line'>  <span class="n">path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vi">@python</span><span class="p">)</span>
</span><span class='line'>  <span class="n">windir</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;WINDIR&#39;</span><span class="o">]</span>
</span><span class='line'>  <span class="c1"># Windows Python doesn&#39;t like &#39; with inner &quot; so we have to switch it around. </span>
</span><span class='line'>  <span class="n">winversion</span> <span class="o">=</span> <span class="sx">%x(</span><span class="si">#{</span><span class="vi">@python</span><span class="si">}</span><span class="sx"> -c &quot;import sys; print &#39;%d%d&#39; % sys.version_info[:2]&quot;)</span><span class="o">.</span><span class="n">chomp</span>
</span><span class='line'>  <span class="n">dll</span> <span class="o">=</span> <span class="s2">&quot;python</span><span class="si">#{</span><span class="n">winversion</span><span class="si">}</span><span class="s2">.dll&quot;</span>
</span><span class='line'>  <span class="n">locations</span> <span class="o">&lt;&lt;</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">windir</span><span class="p">,</span> <span class="s2">&quot;System32&quot;</span><span class="p">,</span> <span class="n">dll</span><span class="p">)</span>
</span><span class='line'>  <span class="n">locations</span> <span class="o">&lt;&lt;</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">windir</span><span class="p">,</span> <span class="s2">&quot;SysWOW64&quot;</span><span class="p">,</span> <span class="n">dll</span><span class="p">)</span>
</span><span class='line'>  <span class="n">locations</span> <span class="o">&lt;&lt;</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">dll</span><span class="p">)</span>
</span><span class='line'>  <span class="n">locations</span> <span class="o">&lt;&lt;</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">&quot;libs&quot;</span><span class="p">,</span> <span class="n">dll</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
</feed>
